从0到1完全掌握 CSRF
#本文作者:Drunkbaby, 转载自FreeBuf。
原文链接:https://www.freebuf.com/articles/web/333173.html
仅供技术分享交流学习,侵权请联系我们删除。
从0到1完全掌握CSRF
二刷漏洞:知其所以然 -> 知其然 -> 懂其攻 -> 知其守
0x01 前言
总觉得自己的 CSRF 掌握的挺不好的,如今二刷一遍。
当初一刷的时候用的是 Port,而毕竟 Port 嘛,更加注重的是漏洞挖掘,所以当时只是简单地会用 Burpsuite 当中的 CSRF Poc 而已,其余的原理阿,防御措施阿,都不太懂。
0x02 什么是 CSRF
面试的时候的著名问题:"谈一谈你对 CSRF 与 SSRF 区别的看法"
这个问题,如果我们用非常通俗的语言讲的话,CSRF 更像是钓鱼的举动,是用户攻击用户的;而对于 SSRF 来说,是由服务器发出请求,用户日服务器的。
CSRF(Cross-site request forgery)跨站请求伪造:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。
在 Port 中,原理图是这样的
我们在学习 CSRF 攻击之前好好先阐述一下它的原理
一个典型的CSRF攻击有着如下的流程:
受害者登录 a.com,并保留了登录凭证(Cookie)。
攻击者引诱受害者访问了 b.com。
b.com 向 a.com 发送了一个请求:a.com/act=xx。浏览器会默认携带 a.com 的 Cookie。
a.com 接收到请求后,对请求进行验证,并确认是受害者的凭证,误以为是受害者自己发送的请求。
a.com 以受害者的名义执行了 act=xx。
攻击完成,攻击者在受害者不知情的情况下,冒充受害者,让 a.com 执行了自己定义的操作。
是不是感觉这个工作流程和 XSS 有些类似,但是 XSS 与 CSRF 的最大区别在于对 Cookie 的使用,XSS 的把受害者 的 Cookie 偷盗过来,而 CSRF 则是借用了受害者的 Cookie。
下面我们举个例子深化一下 CSRF 的原理。
0x03 CSRF 实战场景(原理应用)
本段内容摘自美团技术团队文章
这一天,小明同学百无聊赖地刷着 Gmail 邮件。大部分都是没营养的通知、验证码、聊天记录之类。但有一封邮件引起了小明的注意:
甩卖比特币,一个只要998!!
聪明的小明当然知道这种肯定是骗子,但还是抱着好奇的态度点了进去(请勿模仿)。果然,这只是一个什么都没有的空白页面,小明失望的关闭了页面。一切似乎什么都没有发生……
在这平静的外表之下,黑客的攻击已然得手。小明的 Gmail 中,被偷偷设置了一个过滤规则,这个规则使得所有的邮件都会被自动转发到 hacker@hackermail.com(也就是攻击方的邮箱)。小明还在继续刷着邮件,殊不知他的邮件正在一封封地,如脱缰的野马一般地,持续不断地向着黑客的邮箱转发而去。
不久之后的一天,小明发现自己的域名已经被转让了。懵懂的小明以为是域名到期自己忘了续费,直到有一天,对方开出了 $650 的赎回价码,小明才开始觉得不太对劲。
小明仔细查了下域名的转让,对方是拥有自己的验证码的,而域名的验证码只存在于自己的邮箱里面。小明回想起那天奇怪的链接,打开后重新查看了“空白页”的源码:
代码解析 ———— 这也是我们后续要讲到的 CSRF Poc
这个页面只要打开,就会向Gmail发送一个post请求。请求中,执行了“Create Filter”命令,将所有的邮件,转发到“hacker@hackermail.com”。
小明由于刚刚就登陆了Gmail,所以这个请求发送时,携带着小明的登录凭证(Cookie),Gmail的后台接收到请求,验证了确实有小明的登录凭证,于是成功给小明配置了过滤器。
黑客可以查看小明的所有邮件,包括邮件里的域名验证码等隐私信息。拿到验证码之后,黑客就可以要求域名服务商把域名重置给自己。
这个页面只要打开,就会向Gmail发送一个post请求。请求中,执行了“Create Filter”命令,将所有的邮件,转发到“hacker@hackermail.com”。
小明由于刚刚就登陆了Gmail,所以这个请求发送时,携带着小明的登录凭证(Cookie),Gmail的后台接收到请求,验证了确实有小明的登录凭证,于是成功给小明配置了过滤器。
黑客可以查看小明的所有邮件,包括邮件里的域名验证码等隐私信息。拿到验证码之后,黑客就可以要求域名服务商把域名重置给自己。
0x04 CSRF 的攻击方式
上文中,我们明晰了一下 CSRF 的攻击原理,下面我们主讲漏洞挖掘。
1. GET 请求产生的 CSRF
GET 请求产生的 CSRF 较为简单,有 href 攻击的方式与 HTTP 请求的方式。
GET 请求的 href 类 CSRF
View my Pictures!
在已经登录了bank.com
的情况下,当我们点击 "View my Pictures" 这一链接时,就会将钱从一个账户转移到另一个账户,数额为 100000
GET 请求的 HTTP 发包 CSRF
一般会这样利用:
![](url/withdraw?amount=10000&for=hacker)
在受害者访问含有这个img的页面后,浏览器会自动向http://bank.example/withdraw/account=xiaoming&amount=10000&for=hacker
发出一次 HTTP 请求。在攻击者接收到请求的时候我们便可以“借用”对方的 Cookie。
2. POST 请求产生的 CSRF
POST 请求所产生的 CSRF 是我们利用地最多的攻击方式。
这种类型的 CSRF 利用起来通常使用的是一个自动提交的表单。
访问该页面后,表单会自动提交,相当于模拟用户完成了一次 POST 操作。
POST 类型的攻击通常比 GET 要求更加严格一点,但仍并不复杂。任何个人网站、博客,被黑客上传页面的网站都有可能是发起攻击的来源,后端接口不能将安全寄托在仅允许 POST 上面。
这里可以通过 Burpsuite 自带的 CSRF Poc 工具进行攻击,不过在使用的时候也有一些小技巧。
基础的 CSRF 攻击体验
对应可以尝试的靶场,在这靶场当中,并没有添加任意的 CSRF 防御
靶场地址 Lab: CSRF vulnerability with no defenses
因为 CSRF 本质上是一种钓鱼,所以我们也需要第三方网站攻击,如自己的服务器,或者 Burpsuite 靶场自带的 Exploit server;WebGoat 的 WebWolf。
我们先登录进靶场当中,发现有一功能点 ———— Update email
试想一下,我们账号进行了 Update email 的操作。
若我们在自己的服务器上面挂上恶意的 Payload,诱导他人点击之后。相对应的,对方的邮箱也会变成我们 CSRF Poc 所指定的,这样子一来,我就可以通过 "忘记密码" 这种服务来获取他账户的权限了。(当然这里有个前提,对方是登录过的且 Cookie 处于生效期间)
Exploit 部分
用 Burpsuite 自带的 CSRF Poc 构造出基本框架;
然后我们把这个核心的表单拿出来,并加以修改,构造成最后的 POC
再放入到 Exploit Server 当中,点击 Deliver it to victim 即可。
在对方未对 CSRF 进行任何防御的时候,上述两种 CSRF 攻击方式能够通杀。
懂其攻 -> 知其守
我们现在已经知道 CSRF 攻击方式了,接下来着重讲一讲 CSRF 的防御手段以及绕过方式。
0x05 CSRF 的防御手段
主流的 CSRF 防御手段有以下两种
ban 掉不明域外访问 ———— 使用同源检测与 Samesite Cookie
多加一层验证手段 CSRF Token
1. 接近无敌的防御手法 CSRF Token
如果通俗易懂地解释一下 CSRF Token 的工作原理的话是这样的。
CSRF Token 每随着页面被操作,Token 都会改变,比如 f5 刷新,点击按钮等等,都会导致 CSRF Token 变化。
而每一个请求的 CSRF Token 会通过后端代码验证 Token 的有效性 ———— 是否正确,在时间戳上是否有效,如果加密字符串一致且时间未过期,那么这个Token就是有效的。
以 Java 为例,我们介绍一下 CSRF Token 服务端的校验逻辑
HttpServletRequest req = (HttpServletRequest)request; HttpSession s = req.getSession();
// 从 session 中得到 csrftoken 属性
String sToken = (String)s.getAttribute(“csrftoken”); if(sToken == null) {
// 产生新的 token 放入 session 中
sToken = generateToken(); s.setAttribute(“csrftoken”,sToken); chain.doFilter(request, response);
}
else {
// 从 HTTP 头中取得 csrftoken
String xhrToken = req.getHeader(“csrftoken”);
// 从请求参数中取得 csrftoken
String pToken = req.getParameter(“csrftoken”);
if(sToken != null && xhrToken != null && sToken.equals(xhrToken)){
chain.doFilter(request, response);
}
else if(sToken != null && pToken != null && sToken.equals(pToken)){
chain.doFilter(request, response);
}
else
{ request.getRequestDispatcher(“error.jsp”).forward(request,response);
}
}
2. 用的较少的限制同源
Samesite 是 Set-Cookie 的一种属性,它有三个值
Strict
最为严格,完全禁止第三方 Cookie,跨站点时,任何情况下都不会发送 Cookie。换言之,只有当前网页的 URL 与请求目标一致,才会带上 Cookie。
Set-Cookie: CookieName=CookieValue; SameSite=Strict;
Lax
规则稍稍放宽,大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 Get 请求除外。
Set-Cookie: CookieName=CookieValue; SameSite=Lax;
还有一种属性为None
,这种属性代表关闭了SameSite
一般攻击要进行绕过可以尝试将 SameSite 设置为 None
这种方法的原理也比较简单,因为 CSRF 的本质也是钓鱼,比如我通过邮箱发送钓鱼邮件,那么这时候的 "源" 就是邮箱界面。
如果服务器设置了严格的同源政策,将不接收来自邮箱这一 "源" 的请求。
ok,讲完了常规的防御手段,接下来我们聊聊绕过
0x06 针对 CSRF Token 与同源政策的绕过手段
我们的绕过手段是基于 CSRF Token 或同源政策并不是那么严格的情况下,甚至某些时候由于设计的疏忽产生的逻辑漏洞。
若非特别提醒,以下所有靶场的业务点均处于 "Update email"下。
1. 将 POST 修改为 GET 请求进行绕过
靶场地址 Lab: CSRF where token validation depends on request method)
背后逻辑:
CSRF Token 在 POST 请求当中生效,且业务点并非完全限制请求为 POST 请求,从而给了攻击者进行绕过的机会。
探测方法:将 CSRF Token 删掉,并将 HTTP 请求修改为 GET 请求。
此时的回显为"Missing parameter 'csrf'"
,我们再将 HTTP 请求修改为 GET 请求,观察回显。
回显 302,代表我们可以用这种方式进行绕过,构造 Payload
2. 删除 CSRF Token 进行绕过
靶场地址 Lab: CSRF where token validation depends on token being present
背后逻辑:
并没有强验证 CSRF Token 的存在性。
我们尝试删除 CSRF Token,回显 302
在删除掉 CSRF Token 之后生成 POC 即可。
3. CSRF Token 未与用户 Session 绑定
靶场地址 Lab: CSRF where token is not tied to user session
背后逻辑
未进行严格的一一身份对应,这其实很好理解。举个例子,我们登录注册界面,实际上是需要去匹配用户名与密码是否相等的,而这里的逻辑也是一致。
那么这里,我可以先修改 Cookie,再修改 CSRF Token,来观察 CSRF Token 与 Cookie 是否对应了,或者说是否绑定了。
修改 Cookie 中的 Session 值,观察回显为 "Unauthorized"
修改 CSRF Token,观察回显为 "Invalid CSRF Token"
说明 CSRF Token 并未与 session 绑定,而是与 csrfKey(也就是 value) 绑定的,根据 cookie 的传递性,我们可以在其他页面提前把 csrfKey 注入进去,这里我们利用img
与onerror
组合的 XSS 以及 CLRF 技术来构造 CSRF。
这里借用梨子师傅的 Poc
当受害者点击 CSRF 链接时会先触发 CLRF 注入 Set-Cookie 参数值,将 csrfKey 值添加到 Cookie 中,然后再用附有与 csrfKey 对应的 CSRF Token 的请求去提交修改邮箱请求。
4. 当 Cookie 中的 CSRF 值与 CSRF Token 的值一致时
靶场地址 Lab: CSRF where token is duplicated in cookie
背后逻辑:
只是将 CSRF Token 简单复制到 cookie 头中,然后仅验证两者是否一致。
所以这里我们的绕过 Poc 的核心部分应该是这样的,%0d%0a
为\r\n
,也就是 CR 与 LF
5. 对不严格的 Referer 限制进行绕过
靶场地址 Lab: CSRF with broken Referer validation
背后逻辑
并没有特别严格地限制 Referer,仅仅只是不允许了这一种的 Referer。
Referer: 靶场地址.com
// 下面是非法的
Referer: baidu.com
一般我们通过Referer: baidu.com
来判断 Referer 的限制。
若Referer: baidu.com
被限制,则我们可以通过这种方式进行绕过
http://attacker-website.com/csrf-attack?baidu.com
靶场部分,同样是对更改邮箱这个功能点进行 CSRF 攻击
这里我们需要介绍一下history.pushState
,这个函数顾名思义,就是插入历史记录的,所以这也就是为什么第三个参数的值修改为与攻击链接同源后即可绕过错误地 Referer 头验证机制,所以我们这样构造 CSRF 页面。
我们先修改 Referer 为 baidu.com 查看回显,成功发包。
修改 Referer 为baidu.com+?laburl
,回显为 302 成功。
构造 Payload,将history.pushState
的第三个参数修改为 Lab 的 URL 地址。投放之后,在 Head 当中添加Referrer-Policy: unsafe-url
0x07 小结
CSRF 攻击本质上还是一种钓鱼手段,本文着重讲了一些 CSRF 攻击的绕过手法,说不定渗透的时候多试一试就能起到意想不到的效果。