Published On: 18 Jul 2019
This article is about how I found a vulnerability on Instagram that allowed me to hack any Instagram account without consent permission. Facebook and Instagram security team fixed the issue and rewarded me $30000 as a part of their bounty program. Source : https://thezerohack.com/hack-any-instagram
Facebook is working constantly to improve its security controls on all of their platforms. As a part of it, they recently increased reward payouts for all critical vulnerabilities including account takeovers. So I decided to try my luck on Facebook and Instagram. Fortunately, I was able to find one on Instagram.
An attacker could bypass a rate limit on an endpoint used to verify recovery codes requested through Instagram's mobile account recovery flow.
Step
1
I tried to reset my password on the Instagram web interface. They have a link based password reset mechanism which is pretty strong and I couldn’t find any bugs after a few minutes of testing.
Step
2
Then switched to their mobile recovery flow, where I was able to find a susceptible behavior. When a user enters his/her mobile number, they will be sent a six-digit passcode to their mobile number. They have to enter it to change their password. Therefore if we are able to try all the one million codes on the verify-code endpoint, we would be able to change the password of any account. But I was pretty sure that there must be some rate limiting against such brute-force attacks. I decided to test it.
Step
3
My tests did show the presence of rate limiting. I sent around 1000 requests, 250 of them went through and the rest 750 requests were rate limited. Tried another 1000, now many of them got rate limited. So their systems are validating and rate limiting the requests properly.
Two things that struck mind was the number of requests and the absence of blacklisting. I was able to send requests continuously without getting blocked even though the number of requests I can send in a fraction of time is limited.
Step
4
After a few days of continuous testing, I found two things that allowed me to bypass their rate limiting mechanism.
For those who are unaware of race condition, please read it here. Sending concurrent requests using multiple IPs allowed me to send a large number of requests without getting limited. The number of requests we can send is dependent on concurrency of reqs and the number of IPs we use. Also, I realized that the code expires in 10 minutes, it makes the attack even harder, therefore we need 1000s of IPs to perform the attack.
Step
5
Requesting passcode
POST /api/v1/users/lookup/ HTTP/1.1
User-Agent: Instagram 92.0.0.11.114 Android
Accept-Language: en-IN, en-US
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Accept-Encoding: gzip, deflate
Host: i.instagram.com
Connection: keep-alive
q=mobile_number&device_id=android-device-id-here
The victim will receive a passcode and it will expire in 10 minutes.
Step
6
Verify passcode
POST /api/v1/accounts/account_recovery_code_verify/ HTTP/1.1
User-Agent: Instagram 92.0.0.11.114 Android
Accept-Language: en-IN, en-US
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Accept-Encoding: gzip, deflate
Host: i.instagram.com
Connection: keep-aliverecover_code=123456&device_id=android-device-id-here
Now we need to brute-force this endpoint using multiple IPs. Roughly, I was able to send 200 requests from a single IP without hitting rate limit.
I have used 1000 different machines (to achieve concurrency easily) and IPs to send 200k requests (that’s 20 percent of total one million probability) in my tests.
In a real attack scenario, the attacker needs 5000 IPs to hack an account. It sounds big but that’s actually easy if you use a cloud service provider like Amazon or Google. It would cost around 150 dollars to perform the complete attack of one million codes.