Instagram's One-Click Privacy Switch

Reading time ~2 minutes

Back in April I found three CSRF issues on Instagram, stemming from their Android/iOS App API (which is slightly different from their public API - it’s hosted on their main domain and doesn’t need an access token).

These issues were present in the following end-points:

  • accounts/remove_profile_pic - This is used to remove the profile picture from an account
  • accounts/set_private - This is used to mark a profile as private
  • accounts/set_public - This is used to mark a profile as public

Obviously the best one out of these is accounts/set_public. With a simple GET request we can reveal anyones profile and access their private pictures. Pretty cool.

Facebook patched the holes pretty quickly and I was awarded a decent bounty for it.

Once patched I checked to make sure that it was indeed fixed, and issuing a GET request returns a 405 Method Not Allowed response.

Round Two

I didn’t blog about the issue and completely forgot about it until recently. I decided to have another look at the Android App to see if there was any new end-points to play around with.

Pretty much all API requests within the app call a method named setSignedBody. This generates a hash of the parameters with a secret embedded in an .so file, meaning we can’t craft our own request on-the-fly and submit on the users behalf (without extracting the secret).

However, the three end-points I submitted still didn’t use setSignedBody (presumably because there are no parameters needed), and therefore no token is sent along. Because of this, we can submit a POST request and still perform the attack which was supposed to be fixed!

The use of setSignedBody without a CSRF token means that all end-points are vulnerable to a replay attack. You simply submit the request yourself, catch the request in Burp, and replay to the victim. Unfortunately, this is something I realised after the bug was fixed, so no screenshots available.

So the moral here is that you should double-double-check that an issue is fixed. If I’d been more thorough in testing the fix I would have spotted it sooner than four months, my bad.


This is now patched by requiring all requests to have a csrftoken parameter. Any request which is signed also requires a _uid parameter to prevent replay attacks (unless you extract the secret…).

The original proof-of-concept now returns a 400 error.

The response body is a JSON object showing the error message.

Obtaining Login Tokens for an Outlook, Office or Azure Account

This is pretty similar to Wes's awesome OAuth CSRF in Live, except it's in the main Microsoft authentication system rather than the OAuth...… Continue reading