During my recent testing of Instagram’s mobile API security measures, I identified a critical flaw in the platform’s account recovery and challenge system. What initially looked like a standard missing rate limit on a 6-digit verification code endpoint actually turned out to be a high-impact business logic vulnerability.
By abusing the code_entry_async endpoint without triggering any rate limits, an attacker could not only brute-force the verification process but completely poison the backend verification state. The result? A permanent Denial of Access condition where even the legitimate account owner is entirely locked out, with the server rejecting fresh, valid verification codes across all official channels.
Technical Overview & Architecture Flow
When Instagram detects unusual activity (e.g., a session from a new device or suspicious location), it locks the account and requires the owner to verify their identity via a 6-digit code sent to their registered email.
The Android API endpoint responsible for processing this submission—com.instagram.challenge.navigation.take_challenge/code_entry_async (also routed via com.bloks.www.ap.two_step_verification)—lacked basic brute-force protections. But more critically, bombarding this endpoint with incorrect attempts forces the backend into a corrupted state at the User Identity Level.
The Impact: From Brute-Force to Persistent Lockout
Normally, a missing rate limit allows an attacker to guess the code (since there are only 1,000,000 possible combinations). However, my testing revealed a much more destructive impact.
After submitting ~1,000 incorrect codes in rapid succession (~3 minutes), the endpoint never returned a 429 Too Many Requests, a CAPTCHA, or a session block. Every single request returned a standard HTTP 200 with the message: “This code doesn’t work. Check it’s correct or try a new one.”
To test the true impact, I generated a real 6-digit code to my email and intentionally ran my script to submit 1,000 incorrect attempts before submitting the actual correct code.
Here is exactly what happened:
- The server accepted all 1,000 incorrect attempts with standard HTTP 200 responses.
- When the script submitted the actual correct code, the server rejected it.
- I entered the exact same correct code manually in the official Instagram app—it still failed.
- Even 48 hours later, when requesting a brand-new code and entering it via the official
instagram.com/hackedrecovery portal, the server persistently rejected the valid code.
This means an external attacker could intentionally poison the backend session for any locked account, permanently severing the victim’s ability to recover their profile.
Proof of Concept (PoC)
Step 1: Trigger Account Lock & Intercept Token
Log into the locked account using the Instagram Android app via an HTTP proxy. Call challenge_picker.async to trigger the EMAIL challenge and extract the context_data token.
Step 2: Brute-Force Code Submission
Without knowing the code, call code_entry_async repeatedly.
POST /api/v1/bloks/apps/com.bloks.www.ap.two_step_verification.code_entry_async/ HTTP/1.1
Host: i.instagram.com
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
User-Agent: Instagram 309.1.0.41.113 Android (33/13; 480dpi; 1080x2304; Blackview; SHARK 8; ...)
params={"client_input_params":{"code":"000000","machine_id":"","device_id":"[DEVICE_UUID]","family_device_id":"[FAMILY_UUID]","auth_secure_device_id":"","aac":"[AAC_JSON]"},"server_params":{"context_data":"[CONTEXT_DATA_TOKEN]","is_resend_code":"0"}}&bk_client_context={"bloks_version":"[BLOKS_VERSION_HASH]","styles_id":"instagram"}&bloks_versioning_id=[BLOKS_VERSION_HASH]
Step 3: Server Validates Incorrect Code (No Limits)
The server processes the wrong code and responds without dropping the session:
{
"layout": {
"bloks_payload": {
"tree": {
"㐟": {
"#": "(bk.action.bloks.WriteGlobalConsistencyStore, \"BLOKS_AUTH_PLATFORM_ENTER_CODE:error_message\", \"This code doesn't work. Check it's correct or try a new one.\")"
}
}
}
},
"status": "ok"
}
The script continues from 000000 to 001000 with zero rate limiting or throttling. Once the limit threshold is crossed on the backend, the account state is permanently poisoned.
Resolution and Fix
After discovering the vulnerability, I reported it through Meta’s Bug Bounty Program, providing detailed reproduction steps and proof of concept. Meta acknowledged the issue and deployed a fix to properly enforce privacy controls across legacy query paths.