Friday, March 1, 2013

OAuth1, OAuth2, OAuth...?

TL;DR OAuth2 sucks.

Please don't think about OAuth2 as about the next generation of OAuth1. They are completely different like colors: OAuth1 is the green version, OAuth2 is the red version
The biggest OAuth1 provider - Twitter.
I bet ($100!) they are not switching to OAuth2 in the near future. Pros and cons:
+ becoming compatible with the rest of social networks
- making authorization flow insecure, like the rest of social networks

I am not telling OAuth1 is super secure — it was vulnerable to session fixation a few years ago. If you made user to approve 'oauth_token' issued for your account, then you could use same oauth_token again and sign in his account on the Client website.

It was fixed in oauth1.a. Wait, read again: it was fixed. None of oauth2 vulnerabilities i pointed out in my previous posts a year ago was adressed in the spec. OAuth1 is straight, concise, explicit and secure protocol. OAuth2 is the road to hell.
Here we go!

OAuth2 core vulnerabilities - parameters



I have no idea why, who and, generally speaking, what the fuck, but we can transfer "response_type" as a parameter in URL (not as a setting of your Client).

Vector 1. Set respones_type=token in authorization URL and use specially crafted redirect_uri to leak access_token from URI fragment. You can add Chrome vulns like we did to hack Facebook. Hashbang #! is very nice bug too. Controlled piece of code (XSS) or mobile app like Nir did. BTW avoid pre-approved Clients ( Facebook Messenger with full permissions)

Stolen access_token = Game Over.

response_type must be a constant value in application settings.

redirect_uri
Vector 2. If spec was implemented properly then tampering redirect_uri to other, "leaky", values is pointless. Because to obtain access token you must send redirect_uri value with client creds. If actual redirect_uri was "leaky" and not equal real redirect_uri Client will not be able to obtain access_token for this code.

Vk (vkontakte) was vulnerable to this attack until Sep 2012 - redirect_uri wasn't required to obtain token. An <img> on client website could leak victim's 'code' through referrer and attacker could use same 'code' to log in victim's account.


redirect_uri should be a constant value in application settings.

'scope' (actions User should allow for your Client) is also transfered in query string. I can't call it a major vulnerability, rather ugly user experience but anyway this is sort of stupid, no? "Pay as much as you wish, up to you"

Checking permissions after obtaining access_token is barely a right solution. This is rather a work around. Much better:

  1. if it's a parameter in URL then User should be able to remove some permissions by clicking [X]. This is my human rights, fuck yeah!
  2. if you really need some scope so badly - set it in Client's settings. Now you are 100% sure that User granted these permissions.

Authentication vs Authorization

Wiki
authorize = permit 3rd party Client to access your Resources (/me, /statuses/new)
authenticate = prove that "this guy" is "that guy" which has account on the website.

OAuth2 was designed for authorization purposes only, but now in 90% (yep!) of cases people authenticate with it too! They simply use /me endpoint and find a record in database by external_user_id and provider_name.

It leads to bad things happenning.

Vector 3The Most Common OAuth2 Vulnerability or how I found CSRF based account hijacking vuln in most of OAuth2 implementations (omniauth, django etc)
At some extent state-by-default is attr_accessible-by-default (you know what i mean). I think state should be a compulsory parameter. Otherwise we can invent a new compulsory csrf_token parameter doing exactly same job with "mnemonic" name.
Any implementation w/o 'state' is vulnerable to CSRF. It leads to session fixation (Alice is logged in as Mallory) and account hijacking (Mallory can log in as Alice because Alice connected Mallory Provider Account).

Vector 4One access_token to rule them all or make sure access_token belongs to your Client

I truly love signed_request (Facebook feature!), it provides information about Client that issued this access_token. I think we should stop using response_type=token and use signed_request only. Because it is a reliable way for mobile and client side apps to obtain access_token+client_id of this token+current user_id (less round trips).
If we make redirect_uri constant and introduce encrypted_request (same data but encrypted with client_secret) it would be just perfectly secure flow.

XSS Threat, Vector 5
Nowdays XSS cannot steal user's session because of httpOnly flag (I assume everyone uses it).

Wait.. does OAuth2-based authentication have any protection from possible XSS on Client's website? No, attacker gets carte blanche:

  1. create iframe with response_type=token in authorize URL, then slice location.hash containing access_token
  2. use response_type=code and set wrong state. If CSRF protection is implemented 'code' will not be used and attacker can hijack User's account simply using this 'code' (within 10 minutes). Friendly reminder for Providers: 'code' must be used only once and within couple of minutes — I got $2000 bounty from facebook for finding replay attack.
  3. if csrf protection is NOT implemented (wtf?!) you can try a cool trick - 414 error. Set super long state (5000 symbols), facebook has no limits on state but Client server most likely has. 'code' won't be used because of server error - slice it!
  4. 'state' fixation + using callback with Attacker's code to hijack account
Voila, both stolen access_token + stolen account on Client
Mitigations:

  1. always obtain access_token for every received code. But don't use it if state was wrong. "Destroy" code, or attacker will use it.
  2. clear state cookie as soon as it was used. "Destroy" state or attacker will use it.
If there is session fixation on Client website (I often seen absence of CSRF token on login forms) then Provider account fixation is routinely possible


Phishing Threat, Vector 6
Can you imagine how simple phishing is: create a Client with slightly different name: e.g. Skype -> Skype App/Skype IM, copy picture, description from original Client, use skype.com.evil.com domain.
Mitigations:
  1. Verified Clients. Similar popup near Client name, like we have for twitter accounts.
  2. Add information about amount of Users this app has. I won't authorize "Skype IM" having only 12 users with such app installed.

Client Leaked All the Tokens.
What is Provider gonna do if some Client leaked all access_tokens at once (SQL injection for example). I think there should be kind of Antifraud system, monitoring token usage. And I am developing one btw, let me know if you wish to try.

'refresh_token'
I might be missing something, but why can't we use access_token + client creds to obtain a new access_token? refresh_token = access_token. If access_token is stolen it cannot be refreshed w/o client creds anyway. If client creds are stolen too = Game Over.

Infinite access
Dear Providers, please, add valid_thru parameter, I want to set for how long the Client has access to my Resources.


Last but not least threat, MITM
SSL and encryption. OAuth2 relies heavily on https. This makes framework simpler but way less secure.

so, OAuth...?

This is a sad story. And I don't know what to do and how to fix it.
Who to join? OAuth2.a? i am just a russian scriptkiddie nobody listens to :/

Framework, not protocol, they said.

coming a mess big frameworks authorization in

25 comments:

  1. Your blog has been added to the InfoSecMash.com newsfeed and our Google Currents #InfoSec magazine.

    ReplyDelete
  2. I thought the web was safe - before I found this great blog. Keep posting! Very interesting articles.

    ReplyDelete
  3. "Stolen access_token = Game Over."

    Ummm no. The whole point of OAuth2 tokens is that they're very short lived, so having one leak isn't really 'Game Over". Getting your hands on a Refresh Token would be "Game Over".

    I feel your frustration with the OAuth2 spec, but ultimately it comes down to implementing it with best practices in mind. A secure OAuth2 implementation won't allow arbitrary redirect_uri's to be used, will have very short lived access tokens, will use refresh tokens, and will only allow response_types that make sense for how clients and users aught to behave.

    Most of the other vectors you explain are not caused by OAuth2 at all, but are associated with a poor implementation of it. Is it really fair to blame the OAuth2 spec for not escaping HTML? For users getting phished?

    A mitigation for the phishing threat, by the way, would be to show the user who created the client and prove that they're a "trusted vendor" or something.

    ReplyDelete
    Replies
    1. >Ummm no. The whole point of OAuth2 tokens is that they're very short lived, so having one leak isn't really 'Game Over". Getting your hands on a Refresh Token would be "Game Over".

      ha, try facebook and response_type token. access token you will get will never expire

      >A secure OAuth2 implementation won't allow arbitrary redirect_uri's to be used
      how to make a "secure" oauth2 impl.? Is there "Making it Secure" paragrpah in spec?

      > will only allow response_types that make sense for how clients and users aught to behave
      it's cool to have only needed response_tokens, but wait, if i need both code/token do i still can set it as URL param? Maybe here is the problem - it must not be a param anyway and spec should explain it.

      >t. Is it really fair to blame the OAuth2 spec for not escaping HTML?
      Oauth2 now claims to be wide used authentication tool. No way back. And it should try hard to help clients, even if it's client's fuckup : XSS, CSRF w/o state etc.

      >A mitigation for the phishing threat, by the way, would be to show the user who created the client and prove that they're a "trusted vendor" or something.
      yeah, that's what i proposed - verified clients.

      Delete
    2. P.S.
      leaking access token is game over for short lived too.
      If it is still active who cares it is active for 2 minutes or forever? I only need 5 seconds to spam on your wall and steal your friends graph. Short lived != safe

      Delete
    3. @Egor, FB Access Tokens expire in about an hour (you can extend it to two months but you need app secret to do so). However, I agree that you just need seconds to fetch user's e-mail, private data and minutes to even get whole user's stream history.

      Delete
    4. double checked - 3687 seconds
      i recall a few months ago it was 0!

      anyway, it's perfectly enough to do bad things

      Delete
    5. I've got to give credit to this point "If it is still active who cares it is active for 2 minutes or forever?"

      The argument of "... they're very short lived, so having one leak isn't really 'Game Over'" sounds more likely from developers rather than from a security professional.

      Delete
  4. "use response_type=code and set wrong state. If CSRF protection is implemented 'code' will not be used and attacker can hijack User's account simply using this 'code' (within 10 minutes)."

    Since you don't have app secret you can't do it (https://github.com/facebook/facebook-php-sdk/blob/master/src/base_facebook.php#L775). I didn't check but I'm pretty sure code is bounded with specific app id. Also, how would you obtain this code anyway? If site has X-Frame-Options: DENY you can't embed it in iframe. If it hasn't, you still can't get this iframe URL after redirection.

    ReplyDelete
    Replies
    1. >If site has X-Frame-Options: DENY you can't embed it in iframe
      so what, window.open

      > Since you don't have app secret you can't do it
      I don't need app secret. I simply use exactly same code on same callback as an attacker within 10 min

      Delete
  5. > so what, window.open

    Again, you can't get query param from URL if window has redirected. Am I right?

    > I don't need app secret. I simply use exactly same code on same callback as an attacker within 10 min

    OK, but how would you obtain it? Man in the middle?

    ReplyDelete
    Replies
    1. this is part of "XSS Threat" paragraph. when I find XSS on client I open window for every user, with either wrong state or very long state to make 414 error.

      code will not be used, redirect_uri will be proper. Later i just use same code by myself

      >Again, you can't get query param from URL if window has redirected. Am I right?
      no, it's possible — you can try code from the post - in fact there is a few seconds when you can copy it

      Delete
    2. 1 xssed page opens authorize url
      2 authorize url -> callback url with code and state
      3 now website of client with wrong state or very long - code is not used so XSS steals it

      Delete
  6. OK, I missed that it's XSS part. Thanks!

    ReplyDelete
  7. at Vector 5, can you write explicitely, which part is done on the attacker side, which part is done on the Victim's side via XSS/CSRF? I'm confused....thanks.

    ReplyDelete
    Replies
    1. XSS is on Client's website, for example, fbapp.com. All code is executed there as well - stealing code/token and CSRF token stealing too

      Delete
  8. aha, thanks.

    also at 4:

    "I truly love signed_request (Facebook feature!), it provides information about Client that issued this access_token. I think we should stop using response_type=token and use signed_request only. "


    That signed_request, what info does it provide about the client? I though it's purpose is for the client to verify that the response came from facebook or not.

    ReplyDelete
    Replies
    1. yes, but at the same time it provides user_id property - you need less round trips to authenticate current user.

      you should check singature ofc

      Delete
  9. You owe all of us 100$ https://dev.twitter.com/docs/api/1.1/post/oauth2/token

    ReplyDelete
    Replies
    1. ha, this is not auth code flow, only client creds grant type

      Delete
  10. By the way even if (installed app) clients are verified and signed, the clients themselves are still vulnerable to reverse-engineering. So if the "client secret" is stolen, what to do?

    ReplyDelete
    Replies
    1. yes, i can be also mitigated (but it is not).

      http://homakov.blogspot.ru/2013/03/redirecturi-is-achilles-heel-of-oauth.html
      Stolen Client Credentials Threat

      shortly - if redirect urls are whitelisted and not forgeable stolen credentials is not a problem

      Delete
  11. That are the reasons why the original author of OAuth 1 resigned from the project: http://hueniverse.com/2012/07/oauth-2-0-and-the-road-to-hell/
    But meanwhile there is http://openid.net/connect/

    ReplyDelete
    Replies
    1. which is nothing but an oauth2 wrapper

      Delete