Tuesday, September 11, 2012

If it's a CRIME, then I'm guilty

tldr: see bottom for the script that demonstrates what CRIME might do.

A secret crime 

Juliano Rizzo and Thai Duong did it again. Their new attack on SSL called CRIME, just like their previous one, BEAST is able to extract cookie values (to perform a session hijack) from SSL/TLS encrypted sessions. BEAST was a chosen plaintext attack and generally required:
  • Man-in-the-middle (attacker monitors all encrypted traffic)
  • Encrypted connection to attacked domain (e.g. victim uses https://mybank.com ) with cookies
  • Adaptive Javascript code able to send POST requests to attacked domain
Javascript code tried bruteforcing the cookie value character-by-character. The m-i-t-m component was observing the ciphertext, looking for differences, and once it found one, it communicated with the Javascript to proceed to next character.

CRIME should be similar:
By running JavaScript code in the browser of the victim and sniffing HTTPS traffic, we can decrypt session cookies," Rizzo told Threatpost. "We don't need to use any browser plugin and we use JavaScript to make the attack faster, but in theory we could do it with static HTML. (source)
but the details are not yet known, they are to be released later this month at Ekoparty.

However, there are already speculations on what could the attack rely on. In fact, Thomas Pornin at security.stackexchange.com have most likely figured it out correctly. The hypothesis is that Rizzo and Duong abuse data compression within the encrypted connection. It's likely as e.g. Chromium disabled TLS compression recently.

Compression-based leakage

Thanks to Cross Origin Resource Sharing it is possible (and easy) for JS to send POST request with arbitrary body cross domain. One has limited control over request headers though - e.g. the cookie header will be either attached in full or not at all (it's not possible to set cookies cross-domain). But, the attacker can construct request that looks like this:
POST / HTTP/1.1
Host: thebankserver.com
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1
Accept: */*
Cookie: secret=XS8b1MWZ0QEKJtM1t+QCofRpCsT2u
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3

POST / HTTP/1.1
Host: thebankserver.com
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1
Accept: */*
Cookie: secret=?

To put it simply, in the POST body we're duplicating part of POST headers. This should compress very nicely. We would know most of the header values (from browser fingerprinting, navigator object etc.), only the cookie value is unknown.

But, we can bruteforce the first character of the cookie by including it in the POST body (we have no control over headers) after the secret= string. By observing the compressed ciphertext length (man in the middle) for all such requests we should be able to spot the difference in one of them. The ciphertext would be shorter due to better compression (longer string occured twice in the request). Then, communicate with JS to proceed to next character and the process continues until the whole cookie value is bruteforced.

That's the theory, at least.

PoC or didn't happen !

There's no time to repeat the whole man-in-the-middle, adaptive JS, encrypted connection set up, so xorninja wrote a script to check the length of zlib deflated HTTP request strings and deduce the cookie values from there. It didn't work, so I've modified the code by adding an adaptive algorithm (encryption length does not always change, sometimes you have to also mutate the POST body to be certain of a character value etc.)

And it works.
Proof of concept can bruteforce the cookie value based on zlib deflated string length only. Cookies can be of arbitrary lengths.

So, what next?

This PoC would have to be included in the whole SSL/mitm/Javascript BEAST-like context so we can check if it actually works in browsers and leaks real-life cookies. Feel free to experiment. I'm waiting for the actual CRIME disclosure.

3 comments:

MH Swende said...

Cool!
As stamparm points out, it should work equally well to just post "Cookie: secret" (or in fact "Cookie: ", as other cookies may be included aswell, and come before the "secret" one). 

Furthermore, this attack would be able to quickly find out where the user is authenticated by just sending name of the session cookie , e.g. "google_cookie" and compare results with "xoogle_cookie" for a large list of sites, google, twitter, facebook etc.  

kkotowicz said...

That's what I thought too, I started with just the Cookie header in body, but in practice this usually gave away just up to a few bytes. Method I use always reveals the whole cookie.

And yes, the method could give away where you're authenticated, though I guess in mitm  the standard response length, compressed or not, is enough to detect this.

joe said...

You don't need CORS for arbitrary body cross-domain POSTs. You can do:



...