The Most Common OAuth2 Vulnerability
The Most Common OAuth2 Vulnerability

TL;DR
If website uses OAuth multi-logins there is an easy way to log into somebodys account, protection is almost never implemented and people dont take into account that OAuth is also used for authentication.
OAuth2 is an authorization framework. Apparently its very popular now. Disregards its popularity a lot of people dont understand it deeply enough to write proper and secure implementation.
OAuth1.a and OAuth2 are incompatible, some services use former(twitter, wtf, come on!), some latter, some of them have insufficient and poor documentation(in terms of security) etc. It took me a few hours to read OAuth2 draft thoroughly and I found a few interesting vectors. One of them I am exposing in this post.
Its really dangerous but very common vulnerability for multi-login OAuth websites.
A little bit of theory:
- response_type = code is server-side auth flow, should be used when possible, more secure than response_type = token. Provider returns code with Users user-agent and Client sends along with clients credentials the code to obtain access_token. Callback when user is redirected looks like site.com/oauth/callback?code=AQCOtAVov1Cu316rpqPfs-8nDb-jJEiF7aex9n05e2dq3oiXlDwubVoC8VEGNq10rSkyyFb3wKbtZh6xpgG59FsAMMSjIAr613Ly1usZ47jPqADzbDyVuotFaRiQux3g6Ut84nmAf9j-KEvsX0bEPH_aCekLNJ1QAnjpls0SL9ZSK-yw1wPQWQsBhbfMPNJ_LqI
- I remind you, OAuth is all about authorization, not authentication. Whats the difference, you might ask. OAuth just gives to Client access to Users resources on Provider.
But very often Client authenticates you by profile_info resource, thus we can call it authentication framework either.


Hacking, Step-by-step:
- Choose Client which suits hacks "condition" - some site.com(we will use Pinterest as showcase) Start authentication process - click "Add OAuth Provider login". You need to get callback from Provider but should not visit it. Its quite difficult - all modern browsers redirect you automatically. I recommend bundle Firefox + NoRedirect extension.
- Do not visit the last URL(http://pinterest.com/connect/facebook/?code=AQCOtAVov1Cu316rpqPfs-8nDb-jJEiF7aex9n05e2dq3oiXlDwubVoC8VEGNq10rSkyyFb3wKbtZh6xpgG59FsAMMSjIAr613Ly1usZ47jPqADzbDyVuotFaRiQux3g6Ut84nmAf9j-KEvsX0bEPH_aCekLNJ1QAnjpls0SL9ZSK-yw1wPQWQsBhbfMPNJ_LqI#_=_), just save and put it into <img src="URL"> or <iframe> or anything else you prefer to send requests.

- Now all you need is to make the User(some certain target or random site.com user) to send HTTP request on your callback URL. You can force him to visit example.com/somepage.html which contains <iframe src=URL>, post <img> on his wall, send him an email/tweet, whatever. User must be logged in site.com when he sends the request.
Well done, your oauth account is attached to Users account on site.com. - Voila, press Log In with that OAuth Provider - you are logged in directly to Users account on site.com.
Enjoy: read private messages, post comments, change payment details, have lulz, whatever. In fact account is yours now.
After you had enough fun you can just Disconnect that OAuth Provider and log out. Nobody will have an idea what has happened, you left no fingerprints!
How to detect, is certain OAuth implementation vulnerable?
If site doesnt send state param and redirect_uri param is static and doesnt contain any random hashes - its vulnerable.I know at least 10+ popular vulnerable sites: e.g. pinterest, digg, soundcloud, snip.it, bit.ly, stumbleupon etc. If you know more sites - please drop me a line at homakov@gmail.com
Also all Rails + Omniauth are vulnerable. Have fun(about 23,300 results)
updates:
- fixed for django https://github.com/omab/django-social-auth/issues/386
- fixed in omniauth https://github.com/intridea/omniauth/issues/612
- reported and fixed in soundcloud.com
- checking public libraries..
Mitigation:
You are supposed to send special optional param state - any random hash you get back by Provider in Users callback: ?code=123&state=HASH. Before adding OAuth account you MUST verify session[state] is equal params[state].
Classic: Insecure-by-default means insecure. Majority of developers dont use it at all - nobody pays for "optional" weird param :trollface:
MUST READ SECTION.
Notices:
- $_SESSION[state] == $_REQUEST[state] is vulnerable code, was used a while ago in FB examples. Emtpy string equals empty string.
- I recommend you to filter code in logs config.filter_parameters += [:code]
- state should not be equal form_authenticity_token(session[:csrf_token]) in rails
- if you implemented response_type=token flow w/o FB JS library, its most likely vulnerable too.
At the moment I am working on "OAuth2 Security Proposal". The Proposal aims to make OAuth2 safer by changing its policies, rules and workflows. Most of the points are supposed to be backwards compatible but some of them are quite difficult to apply. Stay tuned, I am publishing it in a few days.