细说CSRF

定义

目录

  • 定义
    • 图示
  • 分类
    • GET型
    • POST型
  • 区别XSS与CSRF
  • 漏洞利用条件
  • 理解CSRF的场景
  • 防御手段
    • 验证 HTTP Referer 字段
    • 限制 Session Cookie 的生存周期
    • 在请求地址中添加 token 并验证(Anti-CSRF token)
    • 在 HTTP 头中自定义属性并验证
    • 一些背景知识
  • CSRF漏洞的挖掘
  • 案例
    • 环境介绍
    • GET型
    • POST型
  • 参考

CSRF(Cross-Site request forgery)跨站请求伪造,简单地说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并运行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去运行。这利用了web中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。csrf是业务逻辑漏洞,利用了对关键操作缺少确认机制

你可以这么来理解:攻击者盗用了你的身份,以你的名义发送恶意请求,对服务器来说这个请求是完全合法的,但是却完成了攻击者所期望的一个操作,比如以你的名义发送邮件、发消息,盗取你的账号,添加系统管理员,甚至于购买商品、虚拟货币转账等。

图示

细说CSRF_第1张图片

完成以上第5步后,由于webA不知道第5步的请求是用户发出的,还是webB发出的(因为第5步访问的时候会自动带上用户的Cookie),所以webA会根据用户的权限处理第5步的请求,这样webB就达到了模拟用户操作的目的

分类

GET型

如果一个网站某个地方的功能,比如用户修改邮箱是通过GET请求进行修改的。如:/[email protected] ,这个链接的意思是用户id=1将邮箱修改为[email protected]。当我们把这个链接修改为 /[email protected] ,然后通过各种手段发送给被攻击者,诱使被攻击者点击我们的链接,当用户刚好在访问这个网站,他同时又点击了这个链接,那么悲剧发生了。这个用户的邮箱被修改为 [email protected]

POST型

在普通用户的眼中,点击网页->打开试看视频->购买视频是一个很正常的一个流程。可是在攻击者的眼中可以算正常,但又不正常的,当然不正常的情况下,是在开发者安全意识不足所造成的。攻击者在购买处抓到购买时候网站处理购买(扣除)用户余额的地址。比如:/coures/user/handler/25332/buy.php 。通过提交表单,buy.php处理购买的信息,这里的25532为视频ID。那么攻击者现在构造一个链接,链接中包含以下内容。

<form action=/coures/user/handler/25332/buy method=POST>
<input type="text" name="xx" value="xx" />
form>
<script> document.forms[0].submit(); script> 

当用户访问该页面后,表单会自动提交,相当于模拟用户完成了一次POST操作,自动购买了id为25332的视频,从而导致受害者余额扣除。

区别XSS与CSRF

XSS:利用用户对站点的信任
CSRF:利用站点对已经身份认证的信任

漏洞利用条件

  • 被害用户已经完成了身份认证
  • 攻击者必须了解web APP请求的参数构造
  • 诱使用户触发攻击的指令(社工)
  • 新请求的提交不需要重新身份认证或确认机制

理解CSRF的场景

在执行关键处理前,需要确认该请求是否确实由用户自愿发起。如果忽略了这个确认步骤,就可能出现严重问题,比如用户只是浏览了恶意网站,浏览器就擅自执行关键处理等。引发上述问题的安全隐患被称为跨站请求伪造(CSRF)漏洞,而针对CSRF漏洞进行的攻击就是CSRF攻击。
web应用存在CSRF漏洞时就可能会遭受如下攻击:

  • 使用用户的账号购物
  • 删除用户账号
  • 使用用户的账号发布帖子
  • 更改用户的密码或邮箱地址等

CSRF漏洞造成的影响仅限于应用的关键处理被恶意使用,而像用户的个人信息等就无法通过CSRF攻击窃取。
因此,为了预防CSRF漏洞,就需要在执行关键处理前确认请求确实是由用户自愿发起的。
细说CSRF_第2张图片

防御手段

验证 HTTP Referer 字段

【现实意义不大,可以被篡改】
检查 HTTP Referer 字段是否同域 HTTP Referer 是 header 的一部分。当浏览器向服务端发送请求时,浏览器会带上 Referer,用于告诉服务端请求的来源。一般来说,用户提交的站内请求的来源(也就是 Referer 字段)应该站
地址,当检测到非同域时,有理由怀疑用户受到了 CSRF 攻击。虽然这种方法简单又有效,但是!我觉得这种方法目前已经变得不是那么可靠了。原因有三:
(1)这种方法只能防御来自站外的 CSRF,却无法防御来自站内的 CSRF;
(2) 当从 HTTPS 站点发送请求到 HTTP 站点时,浏览器不发送 Referer,即无法检测请求来源;
(3) 虽然 JavaScript/ActionScript 无法修改 Referer,但是 Referer可以在服务端被伪造,即可以被向可信站点 A 发送请求的危险站点 B 伪造,从而通过检查机制;
细说CSRF_第3张图片

限制 Session Cookie 的生存周期

【现实意义不是很大】
规定如果用户在一段时间内不进行任何操作,服务端就自动销毁 Session,用户再次操作时需要重新登录才能继续操作。因为无法真正做到用户一关闭浏览器服务端就销毁 Session,虽然可以在用户关闭浏览器时给服务端发送一个销毁 Session 的请求,但是当浏览器崩溃或被强制关闭时,销毁 Session 的请求无法发出,服务端就一直会保持着这个 Session

在请求地址中添加 token 并验证(Anti-CSRF token)

CSRF 攻击之所以能够成功,是因为黑客可以完全伪造用户的请求,该请求中所有的用户验证信息都是存在于 cookie 中,因此黑客可以在不知道这些验证信息的情况下直接利用用户自己的 cookie 来通过安全验证。要抵御 CSRF,关键在于在请求中放入黑客所不能伪造的信息,并且该信息不存在于 cookie 之中。可以在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端建立一个拦截器来验证这个 token,如果请求中没有 token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。
这种方法要比检查 Referer 要安全一些,token 可以在用户登陆后产生并放于 session 之中,然后在每次请求时把 token 从 session 中拿出,与请求中的 token 进行比对,但这种方法的难点在于如何把 token 以参数的形式加入请求。对于 GET 请求,token 将附在请求地址之后,这样 URL 就变成 http://url?csrftoken=tokenvalue。而对于 POST 请求来说,要在 form 的最后加上 ,这样就把 token 以参数的形式加入请求了。但是,在一个网站中,可以接受请求的地方非常多,要对于每一个请求都加上 token 是很麻烦的,并且很容易漏掉,通常使用的方法就是在每次页面加载时,使用 javascript 遍历整个 dom 树,对于 dom 中所有的 a 和 form 标签后加入 token。这样可以解决大部分的请求,但是对于在页面加载之后动态生成的 html 代码,这种方法就没有作用,还需要程序员在编码时手动添加 token。
该方法还有一个缺点是难以保证 token 本身的安全。特别是在一些论坛之类支持用户自己发表内容的网站,黑客可以在上面发布自己个人网站的地址。由于系统也会在这个地址后面加上 token,黑客可以在自己的网站上得到这个 token,并马上就可以发动 CSRF 攻击。为了避免这一点,系统可以在添加 token 的时候增加一个判断,如果这个链接是链到自己本站的,就在后面添加 token,如果是通向外网则不加。不过,即使这个 csrftoken 不以参数的形式附加在请求之中,黑客的网站也同样可以通过 Referer 来得到这个 token 值以发动 CSRF 攻击。这也是一些用户喜欢手动关闭浏览器 Referer 功能的原因。

在 HTTP 头中自定义属性并验证

这种方法也是使用 token 并进行验证,和上一种方法不同的是,这里并不是把 token 以参数的形式置于 HTTP 请求之中,而是把它放到 HTTP 头中自定义的属性里。通过 XMLHttpRequest 这个类,可以一次性给所有该类请求加上 CSRFToken 这个 HTTP 头属性,并把 token 值放入其中。这样解决了上种方法在请求中加入 token 的不便,同时,通过 XMLHttpRequest 请求的地址不会被记录到浏览器的地址栏,也不用担心 token 会透过 Referer 泄露到其他网站中去。
然而这种方法的局限性非常大。XMLHttpRequest 请求通常用于 Ajax 方法中对于页面局部的异步刷新,并非所有的请求都适合用这个类来发起,而且通过该类请求得到的页面不能被浏览器所记录下,从而进行前进,后退,刷新,收藏等操作,给用户带来不便。另外,对于没有进行 CSRF 防护的遗留系统来说,要采用这种方法来进行防护,要把所有请求都改为 XMLHttpRequest 请求,这样几乎是要重写整个网站,这代价无疑是不能接受的。

一些背景知识

cookie、session、token的区别

CSRF漏洞的挖掘

1:最简单的方法就是抓取一个正常请求的数据包,如果没有Referer字段和token,那么极有可能存在CSRF漏洞
2:如果有Referer字段,但是去掉Referer字段后再重新提交,如果该提交还有效,那么基本上可以确定存在CSRF漏洞。
3:随着对CSRF漏洞研究的不断深入,不断涌现出一些专门针对CSRF漏洞进行检测的工具,如CSRFTester,CSRF Request Builder等。 以CSRFTester工具为例,CSRF漏洞检测工具的测试原理如下:使用CSRFTester进行测试时,首先需要抓取我们在浏览器中访问过的所有链接以及所有的表单等信息,然后通过在CSRFTester中修改相应的表单等信息,重新提交,这相当于一次伪造客户端请求。如果修改后的测试请求成功被网站服务器接受,则说明存在CSRF漏洞,当然此款工具也可以被用来进行CSRF攻击。

案例

这里以DVWA来介绍CSRF

环境介绍

靶机:win2008+phpstudy(DVWA)
攻击者:kali
受害者:win10(物理机)

源代码:

 
if( isset( $_GET[ 'Change' ] ) ) { 
    // Get input 
    $pass_new  = $_GET[ 'password_new' ]; 
    $pass_conf = $_GET[ 'password_conf' ];  
    // Do the passwords match? 
    if( $pass_new == $pass_conf ) { 
        // They do! 
        $pass_new = mysql_real_escape_string( $pass_new ); 
        $pass_new = md5( $pass_new );  
        // Update the database 
        $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';"; 
        $result = mysql_query( $insert ) or die( '
' . mysql_error() . '
'
); // Feedback for the user echo "
Password Changed.
"
; } else { // Issue with passwords matching echo "
Passwords did not match.
"
; } mysql_close(); } ?>

可以看到,系统只是确认了两次输入的密码一致,没有做其他检查,那就可以直接干它。
最简单的方法:把更改密码的URL传给受害者点击
高明一点的方法:把更改密码的URL写成一个html页面放在自己的web服务器上,诱导对方点击

GET型

1:受害者登陆了DVWA
细说CSRF_第4张图片

2:攻击者登录DVWA获取更改密码的那段代码,新密码是123

/DVWA-master/vulnerabilities/csrf/?password_new=123&password_conf=123&Change=Change

拼凑出的URL

csrf

细说CSRF_第5张图片

3:伪造页面用来诱使受害者点击
细说CSRF_第6张图片

鼠标放在csrf上时,左下角出现对应的地址,就说明准备的可以了
细说CSRF_第7张图片

4:受害者被害
细说CSRF_第8张图片

细说CSRF_第9张图片

POST型

1:受害者登录了DVWA
2:攻击者登录DVWA获取更改密码的那段代码,新密码是456
细说CSRF_第10张图片

3:利用BurpSuite生成CSRF的POC代码,并复制下来
(社区版BurpSuite没有此功能)
细说CSRF_第11张图片

细说CSRF_第12张图片

4:制造一个新的html页面
把上面的POC代码复制下来命令为2.html,放在/var/www/html/目录下
5:受害者访问页面
细说CSRF_第13张图片

细说CSRF_第14张图片

参考

Web漏洞 | CSRF(跨站请求伪造漏洞)
《信安之路 2017 年年刊》

你可能感兴趣的:(渗透测试)