Introduction
In my previous article, I shared how a simple Response Manipulation (changing false to true) could bypass OTP screens. It was a classic "low-hanging fruit." However, as developers become more aware of these basic flaws, they've started implementing more robust server-side checks.
But here's the secret: Complexity is the enemy of security. The more complex the OTP logic becomes, the more likely there is a "logic flaw" hiding in the shadows. In this Part 2, we will dive into advanced techniques like Race Conditions and Rate Limit Bypasses that I've encountered in recent bug bounty programs.
1. The "Race Condition" Attack (Concurrency)
Imagine an application that allows only 3 failed OTP attempts before locking the account. What if we send 100 attempts at the exact same millisecond?
In many cases, the server checks the "attempt count" and the "OTP validity" separately. If the requests hit the server simultaneously, the database might not update the "attempt count" fast enough, allowing us to brute-force the code even with a limit in place.
- Tools: Burp Suite + Turbo Intruder.
- The Payload: Use a Python script within Turbo Intruder to flood the
/verify-otpendpoint with a list of 4-digit codes. - The Result: If the server is vulnerable, you might see a
200 OKamidst a sea of429 Too Many Requests.
2. Bypassing Rate Limits with Header Spoofing
If a server blocks your IP after 5 attempts, you can often "reset" your identity by spoofing headers that tell the server you are a different user or proxy.
Try adding these headers to your Burp Intruder request:
X-Forwarded-For: 127.0.0.1X-Originating-IP: 127.0.0.1X-Remote-IP: 127.0.0.1X-Remote-Addr: 127.0.0.1
By rotating the IP address in these headers, you can keep guessing the OTP indefinitely.
Case Study: The "Email Change" Logic Flaw
During a recent hunt, I found a unique bypass. The flow was:
- Request OTP.
- Server sends OTP to
user@email.com. - The Flaw: I intercepted the verification request and changed the email parameter to my email.
POST /api/v1/verify-otp
{"email": "attacker@email.com", "otp": "MY_OWN_OTP", "account_to_unlock": "victim@email.com"}
The server verified my OTP for my email but incorrectly granted me a session token for the victim's account. Always look for mismatched parameters!
How to Fix This (Mitigation for Developers)
To close these gaps, developers should:
- Implement Atomic Counters: Use Redis or similar tools to ensure rate limits are checked "atomically" to prevent Race Conditions
- Consistency Checks: Ensure the OTP being verified is strictly tied to the Session ID or User ID, not just an email passed in the request body.
Conclusion
Bug hunting is 10% tools and 90% thinking outside the box. As defenses evolve, our methods must too. If you found this "Part 2" helpful, don't forget to clap and follow for more technical writeups!
Happy Hacking!