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)
= stolen access_token = Game Over
- window.open(fb auth/redirect_uri=site.com/redirector&response_type=token)
- We get header:
- When the browser loads http://site.com/redirector it gets following header
- And now the browser navigates to
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
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.