Stealing Facebook access_tokens using CSRF in device login flow
Facebook has published a way to use OAuth on Internet of Things devices, called Facebook Login for Devices. You can read the documentation here. The usual flow is:
Application requests graph.facebook.com/oauth/device?type=device_code&client_id=1
to retreive a hash code and user_code
The application tells the user to go to facebook.com/device and input the user_code
The user inputs the code and verifies the application through usual OAuth flow. user_code is then connected to application code.
This is the mobile version of device login flow - redirect_uri points to a mobile domain.
The application can now request graph.facebook.com/oauth/device?type=device_token&client_id=1&code=hash_code
to gain the user access_token
The problem lies in step number 3: when user_code is connected to application_code, it is done like so:
As you can see, there is no CSRF protection, so I started building my proof of concept around that:
The attacker requests graph.facebook.com/oauth/device?type=device_code&client_id=1 to get the user_code (abcd) and hash code (4567)
Present a page to victim which will load
The /dialog/oauth will redirect to
which will succeed
The attacker now requests graph.facebook.com/oauth/device?type=device_token&client_id=1&code=4567
to get the access_token of the victim.
To exploit this, attacker would need to know the victim has approved an application which has “Login for Devices” enabled, which makes it fairly hard to abuse - this is a perfect storm vulnerability.
Through further testing, I found out that every app which has “Login from Devices” enabled, and “Web OAuth Login” disabled gets “m.facebook.com/device.php” automagically added as a valid redirect_uri.
Theoretically, if a pre-approved official Facebook application is made for the future iToaster, this bug could have lead to info disclosure on any Facebook account, and perhaps more, depending on the app permissions. It was a long shot, and I found no such application (thanks @phwd for help here).
Facebook has fixed this first by adding a re-confirmation prompt every time you try to use device login, and later by implementing CSRF protection through "state" OAuth parameter:
December 8, 2015: Bug reported
December 9, 2015: Additional information sent, along with a proof of concept page
December 11, 2015: Facebook confirms the vulnerability
February 1, 2016: I ask for updates about the report
February 10, 2016: Facebook informs me they have fixed the issue
February 10, 2016: Nope, message was sent in error
February 18, 2016: Bug is now fixed
Random blog post