Sunday, January 26, 2014

Two "WontFix" vulnerabilities in Facebook Connect

TL;DR Every website with "Connect Facebook account and log in with it" is vulnerable to account hijacking. Every website relying on signed_request (for example official JS SDK) is vulnerable to account takeover, as soon as an attacker finds a 302 redirect to other domain.



I don't think these will be fixed, as I've heard from the Facebook team that it will break compatibility. I really wish they would fix it though as you can see below, I feel these are serious issues.



I understand the business reasons why they might choose so, but from my perspective when you have to choose between security and compatibility, the former is the right bet. Let me quickly describe what these bugs are and how you can protect your websites.

CSRF on facebook.com login to hijack your identity.
It's higher level Most-Common-OAuth-Vulnerability (we attached Attacker's Social Account to Victim's Client Account) but here even Clients using "state" to prevent CSRF are vulnerable.

<iframe name="playground" src='data:text/html,<form id="genform" action="https://www.facebook.com/login.php" method="POST"><input type="hidden" name="email" value="homakov@gmail.com"><input type="hidden" name="pass" value="password"></form><script>genform.submit()</script>'></iframe>

FYI we need data: trick to get rid of Referer header, Facebook rejects requests with cross domain Referers.

This form logs victim in attacker's arbitrary account (even if user is already logged in, logout procedure is trivial). Now to all OAuth flows Facebook will respond with Attacker's profile information and Attacker's uid.

Every website with "Connect your Facebook to main account to login faster" functionality is vulnerable to account hijacking as long as attacker can replace your identity on Facebook with his identity and connect their Facebook account to victim's account on the website just loading CLIENT/fb/connect URL.

Once again: even if we cannot inject our callback with our code because of state-protection, we can re-login user to make Facebook do all the work for us!

Almost all server-side libraries and implementations are "vulnerable" (they are not, it's Facebook who's vulnerable!) : omniauth, django-social-auth, etc. And yeah, official facebook-php-sdk.

(By the way, I found 2 bugs in omniauth-facebook: state fixation, authentication bypass. Update if you haven't yet.)

Mitigation: require CSRF token for adding a social connection. E.g. instead of /connect/facebook use /connect/facebook?authenticity_token=123qwe. It will make it impossible for an attacker to start the process by himself.

Facebook JS SDK and #signed_request
Since "redirect_uri" is flexible on Connect since its creation, Facebook engineers made it a required parameter to obtain "access_token" for issued "code". If the code was issued for a different (spoofed) redirect_uri, provider will respond with mismatch-error.

signed_request is special non-standard transport created by Facebook. It carries "code" as well, but this code is issued for an empty redirect_uri = "". Furthermore, signed_request is sent in a #fragment, so it can be leaked easily with any 302 redirect to attacker's domain.

And guess what — the redirect can even be on a subdomain. of our target! Attack surface gets so huge, no doubt you can find a redirecting endpoint on any big website.

Basically, signed_request is exactly what "code" flow is, but with Leak-protection turned off.

All you need is to steal victim's signed_request with a redirect to your domain (slice it from location.hash), then open the Client website, put it in the fbsr_CLIENT_ID cookie and hit client's authentication endpoint.

Finally, you're logged in as the owner of that signed_request. It's just like when you steal username+password.

Mitigation: it's hard to get rid from all the redirects. For example Facebook clients like soundcloud, songkick, foursquare are at the same time OAuth providers too, so they have to be able to redirect to 3rd party websites. Each redirect to their "sub" clients is also a threat to leak Facebook's token. Well, you can try to add #_=_ to "kill" fragment part..

It's better to stop using signed_request (get rid of JS SDK) and start using (slightly more) secure code-flow with protections I mentioned above.

Conclusion
In my opinion I'd recommend not using Facebook Connect in critical applications (nor with any other OAuth provider). Perhaps it's suitable quick login for a funny social game but never for a website with important data. Use oldschool passwords instead.

If you must use Facebook Connect, I recommend whitelisting your redirect_uri in app's settings and requiring user interaction (clicking some button) to start adding a new connection. I really hope Facebook will change their mind, to stay trustworthy identity provider.

As of Dec 7 2014 Facebook fixed first bug but there's a way to bypass it. I am not going to publish it because I don't want @isciurus to patch it again :)

11 comments:

  1. typical facebook engineering. i don't see them being around much longer

    ReplyDelete
  2. I'm not sure what is worse, this attitude facebook "engineers" have or the beyond shallow conversations with uninteresting friends. I'm sure of one thing, both will continue their demise. It's sadly blind arrogance on their part which is the same thing that turned Friendster and Myspace in to the social networks of yesteryear.

    ReplyDelete
  3. it looks as though if you look close enough on ANY functionality of facebook you can find a vulnerability. just too many vulnerabilities.

    ReplyDelete
  4. Неплохо было бы прочитать это на русском языке

    ReplyDelete
    Replies
    1. ай транслатед http://habrahabr.ru/post/211362/

      Delete
  5. Is this facebook-only or other OAuth2 providers too?

    ReplyDelete
    Replies
    1. Facebook only, but CSRF-login issue is pretty common among other providers. E.g. vk.com

      Delete
  6. > Every website with "Connect your Facebook to main account to login faster" functionality is vulnerable to account hijacking as long as attacker can replace your identity on Facebook with his identity and connect their Facebook account to victim's account on the website just loading CLIENT/fb/connect URL.

    OK, atacker started this connection process and even forced user to log into controlled account. However, Facebook requires user's confirmation of the access scope. How do you force user to click "Okay" button?

    ReplyDelete
    Replies
    1. But user is logged in attackers account which already preapproved that connection.

      Delete
    2. That's what I assumed to happen, however, I don't see a way to pre-approve a connection at FB. I'm trying to reproduce this attack, but I can't. If the app (e.g. website) is approved at FB side, it is connected to a particular website account and, thus, FB account can no longer be linked to another website account. Any hints?

      Delete
    3. > If the app (e.g. website) is approved at FB side, it is connected to a particular website account and, thus, FB account can no longer be linked to another website account

      Ah, no, not necessarily. You can connect account but not follow redirects. So FB will grant access but client website won't know about this, and will not attach it yet.

      Delete