Description

This bug could allow an attacker to link victim's Instagram account to his Facebook page and then have full control of The Instagram account by just making the victim visit a malicious website and without the need of his interaction.
The bug happens due to the logic of the Oauth flow Facebook used to ensure the linking of an Instagram account to a Facebook page.

Details

The Oauth flow return endpoint (https://m.facebook.com/page/instagram/sync/oauthlink/) which resides in Facebook side and which receives the Instagram account "code" , had a nonce parameter which normally avoid some attacks, however there was no confirmation dialog in the Instagram part to accept or refuse the request. This made possible to the attacker to perform this linking of Instagram account to his own Facebook Page after exploiting a Login CSRF and then generate a valid nonce to use in the oauth redirect URL.

Important Note: Facebook return endpoint for this oauth flow, would deny the nonce even if it's a nonce for the current Facebook user. The nonce should be generated for the same session currently used. For that we need to get a nonce for that specific session by using an access_token linked to it.

Explanation

To get all required urls and finally write the exploit, i followed those steps :

1) Login to the attacker Facebook account using Facebook App ( This should generate a request to api.facebook.com/method/auth.login)
We note the access_token in the response of this request.

2) Go to Instagram App on the same phone, Login to any account, Go to Settings, Accounts, Linked Accounts, and click on Facebook. This should generate a request to m.facebook.com/auth.php endpoint. We note this request.

Those two steps are used to get two things:

  • First we get a Facebook access_token associated to a certain login session
  • Instagram App will use the same session (by specifying a session_key) to login the user in Instagram Webview in order to complete the linking process by requesting this URL below (some would call this a Login CSRF bug):

https://m.facebook.com/auth.php?
api_key=882a8490361da98702bf97a021ddc14d

&session_key=REDACTED

&sig=514274a37b4762e9a4210f40717e35cd

&t=1573083437

&uid=REDACTED

&redirect_uri=fbconnect%3A%2F%2Fsuccess

.....

Ps : We use this method (get the Instagram app generated link) because the request to the login endpoint https://m.facebook.com/auth.php have a "sig" parameter which is used to verify that the URL was not modified ( for example to change the session_key and redirect_uri). So even if we know the parameters required for this endpoint, we still need to calculate the right URL signature. I was able to get the way the sig parameter was generated, but it didn't work for this bug.

3) We generate the oauth nonce with the access_token we got in "Step 1". To do that we use Facebook https://graph.facebook.com/graphql endpoint ( This is possible because the access_token used is a first party access_token of Facebook Android/iOS app) :

https://graph.facebook.com/graphql?
doc_id=REDACTED&
method=POST&
access_token=ACCESS_TOKEN&
variables={
"scale": "4",
"nt_context": {
"using_white_navbar": true,
"styles_id": "...",
"pixel_ratio": 4
},
"params": {
"payload": "/ig_sync/connect/?page_id=ATTACKER_PAGE_ID&redirect_uri=https://m.facebook.com/page/instagram/sync/oauthlink/&platform=android&entry_point=settings",
"nt_context": {
"using_white_navbar": true,
"styles_id": "...",
"pixel_ratio": 4
}}}

Ps: Some URL encoding is needed in the variables parameter

4) Now we have all the needed data to perform the attack , we create a script which do the following steps: ( For a successful attack, the victim should be logged in to his Instagram account on Desktop or Mobile ( in Instagram app Webview or mobile browser)