猫头鹰
信安舆情早知道

Django CSRF 防护绕过漏洞分析

0x01 Django CSRF Bypass

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

0x02 使用 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/

用户可以完全控制 referer 中的 path,然后在放入__utmz的时候没有过滤

0x03 不同的 web server 对 Cookie parse 结果的不同

一个正常的Cookie是这样子的 Cookie: param1=value1; param2=value2;

但是很多 web server 也可以接收使用逗号分隔的

Cookie: param1=value2, param2=value2
 Cookie: param1=value2,param2=value2

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

参考

  • https://docs.python.org/3/library/http.cookies.html
  • http://hg.python.org/cpython/file/3.4/Lib/http/cookies.py#l432
  • http://tools.ietf.org/html/rfc2109
  • http://tools.ietf.org/html/rfc2068

例子

1

0x04 不同的浏览器对 Cookie 处理结果的不同

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

而且 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;

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

0x05 综合在一起利用

条件是

  • 网站使用 Google Analytics
  • 网站使用的 server 或后端有 Cookie 解析的问题,比如 Django
  • 网站使用了基于 Cookie 的 CSRF 防护方法

结果

  • 我们可以设置任意的 Cookie 或者覆盖已有的 Cookie
  • 这个网站就有 CSRF 防护绕过的问题

POC

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

  • 打开 Chrome 隐身模式
  • 登录 instagram.com
  • 点击下面的链接,然后稍等
  • 然后你就会关注了 http://instagram.com/black2fan

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

poc

描述

用户已经登录了 instagram.com

让用户访问下面的链接,同时假设他没有访问过blog.instagram.com,也没有这个子域名的__utmz。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/ 这时候,Cookie 就会使用新的 path 和 domain,.instgram.com 就会被设置一个新的 Cookie _utmz=90378079.1401435337.1.1.utmcsr=blackfan.ru|utmccn=(referral)|utmcmd=referral|utmcct=/r/,]csrftoken=x,

这时候,服务器就会认为 Cookie 中的__utmz是错误的格式,CSRF token是x

使用 CSRF token 提交上面的表单

0x06 修复

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

C = cookies.SimpleCookie()
C.load('__utmz=blah csrftoken=x')
C.load('__utmz=blah\x09csrftoken=x')
C.load('__utmz=blah\x0bcsrftoken=x')
C.load('__utmz=blah\x0ccsrftoken=x')

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

  • IE 浏览器会把\x09 \x0b \x0c替换为下划线
  • Chrome 会忽略这种 Cookie
  • Google Analytics 会把空格替换为 %20,当然这不重要,因为其他的 JS 也可能有问题

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

  • 移除 instgram.com 的所有 Cookie
  • 使用 Firefox 打开 http://instagram.com/?utm_source=1&utm_medium=2&utm_campaign=3&utm_term=4&utm_content=5%09csrftoken=x#
  • 刷新页面,你会发现 csrftoken=x

最近 Django 修复了这个问题:

https://www.djangoproject.com/weblog/2016/sep/26/security-releases/

使用了简单的 parse Cookie 的方法:

https://github.com/django/django/commit/d1bc980db1c0fffd6d60677e62f70beadb9fe64a

虽然不太标准,但是已经足够了。

0x07 思考

 

*作者:virusdefender  Mottoin授权整理发布

转载请注明来自MottoIN,未经允许不得转载!MottoIN » Django CSRF 防护绕过漏洞分析

分享到:更多 ()

评论 抢沙发

评论前必须登录!

 

MottoIN 换一个角度看安全

寻求报道联系我们