June 15, 2026
OTP Bypass: How Hackers Break “Unbreakable” OTP Verification
And How You Can Find These Bugs Too
Yamini Yadav_369
6 min read
A few months back, I was testing a shopping app's signup page. You know the drill—enter your phone number, get an OTP, type it in, and you're verified.
I typed in a random 4-digit number just to see what would happen.
The app said, "Invalid OTP."
Normal, right? But then I noticed something. The response time for a wrong OTP was slightly different from… nothing, really. So I tried something else.
I left the OTP field completely empty and just sent the request.
It said, "Verified successfully."
I sat there staring at my screen for a full minute. An empty field bypassed OTP verification on a live shopping app.
That's the day I realized OTP isn't a wall. It's a door, and sometimes that door is just… not locked properly.
In this blog, I'll break down every common OTP bypass technique I know, explained as simply as possible. No jargon. Just logic.
What Even IS OTP Verification, Logically?
Forget the tech for a second. Here's OTP in plain English:
- You enter your phone/email
- Server generates a random number (like 4829)
- The server sends it to you
- The server also saves this number somewhere (database, session, cache)
- You type the number back
- Server checks: "Does what the user typed match what I saved?"
- If yes → Verified. If no → Rejected.
Every single OTP bug exists because step 7 is done incorrectly.
That's it. That's the whole secret. Every method below is just a different way of breaking step 7.
Method 1: No Rate Limiting (The Classic)
What you're testing:
Can you guess the OTP by trying many times?
How to do it:
- Request an OTP
- Open Burp Suite, capture the OTP verification request
- Send it to Intruder
- Set the OTP field as the payload position
- Use a number list from
0000to9999 - Run the attack
What to look for:
- If the app lets you try unlimited times without blocking you
- If a 4-digit OTP (only 10,000 possibilities) can be brute-forced in seconds
Why it works:
A 4-digit OTP has only 10,000 combinations. A computer can try all of them in under a minute if there's no limit. It's like having a 4-digit padlock, and someone has unlimited time to try every combination.
Real impact: Attacker can verify ANY phone number/email without owning it.
Method 2: OTP Doesn't Expire (Time Bomb That Never Explodes)
What you're testing:
Does the OTP work even after a long time has passed?
How to do it:
- Request an OTP
- Wait 30 minutes (or longer)
- Try using that same old OTP
What to look for:
- If the OTP still works after the "expected" time window
- Most apps say "OTP valid for 5 minutes," but never actually enforce it
Why it works:
If the server never checks the timestamp of when the OTP was generated, an old leaked OTP (from logs, screenshots, or shared screens) remains valid forever.
Method 3: OTP Not Invalidated After Use (Reuse Attack)
What you're testing:
Can the SAME OTP be used multiple times?
How to do it:
- Request an OTP and verify successfully
- Try using that same OTP again for another action (like resetting the password again, or logging in again)
What to look for:
- If the OTP works a second time, even after successful verification
Why it works:
A good system should "burn" (delete/mark used) the OTP the moment it's used. If the server forgets to do this, that one OTP becomes a master key for repeated actions.
Method 4: Response Manipulation (The Empty Field Trick)
What you're testing:
What happens if you remove the OTP field entirely or send it as empty?
How to do it:
- Capture the OTP verification request in Burp
- Try these variations:
- Send
otp=(empty value) - Remove the
otpparameter completely - Send
otp=null - Send
otp=0000 - Send
otp=[](empty array — works on some APIs that expect arrays)
What to look for:
- Any response that says "success" or "verified" without an actual correct OTP
Why it works:
Some backend code looks like this (in plain English logic):
"If OTP field exists AND OTP matches → success"
But if you remove the OTP field entirely, sometimes the check gets skipped completely, and the code defaults to "success" because no error was triggered. This is exactly the bug I found in my story above!
Method 5: Status Code / Response Manipulation
What you're testing:
Does the app trust the server's response too much on the frontend?
How to do it:
- Enter a WRONG OTP
- Intercept the response in Burp (use "Intercept response" in Proxy settings)
- The server replies with something like:
{ "status": "failed", "verified": false }{ "status": "failed", "verified": false }- Change it to:
{ "status": "success", "verified": true }{ "status": "success", "verified": true }- Forward the modified response
What to look for:
- If changing
falseittruelets you proceed to the next page (dashboard, account, etc.)
Why it works:
Some apps do verification logic on the frontend (JavaScript) instead of the backend. If the frontend just "trusts" whatever the server says without the server actually blocking access on its end, you can fake the response and walk right through.
Method 6: OTP Leaked in API Response
What you're testing:
Does the server accidentally SEND the OTP back to you?
How to do it:
- Request an OTP
- Look at the response to that request in Burp (not just what's shown on screen)
- Check the JSON body carefully
What to look for:
Sometimes developers leave debug code in production, and the response looks like the following:
{
"message": "OTP sent successfully",
"debug_otp": "4829",
"expiry": "300"
}{
"message": "OTP sent successfully",
"debug_otp": "4829",
"expiry": "300"
}Why it works:
This is a leftover from testing/development. The developer forgot to remove the line of code that sends the OTP back for "debugging purposes." Now, anyone can see the OTP directly in the network tab!
Method 7: OTP Bound to Wrong Identifier (IDOR + OTP Combo)
What you're testing:
Is the OTP checked against the correct user, or just "any valid OTP in the system"?
How to do it:
- Create TWO accounts — Account A (yours) and Account B (victim, can be a test account you control)
- Request an OTP for Account A — note it down
- Now, try to verify Account B using:
- Account B's session/request
- But Account A's OTP
What to look for:
- If Account B gets verified using Account A's OTP, the system isn't checking "does this OTP belong to THIS specific user?"
Why it works:
The backend logic might be
"Check if THIS OTP exists ANYWHERE in the database and is unused"
Instead of the correct logic:
"Check if THIS OTP belongs to THIS specific user/session AND is unused"
This is super common and super dangerous — it can lead to account takeover.
Method 8: Changing the "Identifier" Mid-Flow (Request Smuggling Style)
What you're testing:
Can you request an OTP for YOUR number, then swap the number/email before final verification?
How to do it:
- Start the signup/verification flow with your phone number (you receive the OTP)
- Capture the verification request in Burp
- Change the phone number/email parameter to the victim's number, but keep YOUR OTP in the OTP field
- Send the request
Example request before:
POST /verify-otp
{
"phone": "9999999999", (your number)
"otp": "4829" (OTP you received)
}POST /verify-otp
{
"phone": "9999999999", (your number)
"otp": "4829" (OTP you received)
}Modified request:
POST /verify-otp
{
"phone": "8888888888", (victim's number)
"otp": "4829" (your valid OTP)
}POST /verify-otp
{
"phone": "8888888888", (victim's number)
"otp": "4829" (your valid OTP)
}What to look for:
- If the server says "verified" for the victim's number using YOUR OTP
Why it works:
The OTP check and the "which account gets verified" logic are sometimes separate steps that don't talk to each other. The server checks, "Is 4829 a valid OTP somewhere?" (Yes, it's yours), then separately marks the phone number in the request as verified without re-checking that the OTP actually belonged to THAT phone number.
This is one of the most underrated, high-impact OTP bugs out there.
Method 9: Race Condition on OTP Verification
What you're testing:
What happens if you send the SAME OTP request multiple times at the same moment?
How to do it:
- Get a valid OTP
- In Burp, send the verify-OTP request to "Repeater" multiple tabs (5–10 tabs)
- Use Burp's "Send group in parallel" feature to fire them all at once
What to look for:
- If multiple actions get triggered (like multiple coupon redemptions, multiple account verifications, etc.) from ONE OTP
Why it works:
If the server checks "is this OTP valid?" and "mark OTP as used" as two SEPARATE steps, sending many requests at the exact same time can sneak multiple requests through before the OTP gets marked as "used."
Method 10: OTP via Different Endpoint (API Confusion)
What you're testing:
Is OTP verified differently depending on which API endpoint you use (mobile app vs web vs old API version)?
How to do it:
- Check if the app has multiple endpoints, like:
/api/v1/verify-otp/api/v2/verify-otp/mobile/verify-otp
- Try the SAME bypass techniques (empty field, brute force, etc.) on the OLDER or less-used endpoint
What to look for:
- Older API versions often have WEAKER security checks because developers forget to update them when they fix bugs in the newer version
Why it works:
Security fixes don't always get applied everywhere. If it /v2/ was patched but /v1/ is still live and reachable, it becomes the weak link.
How to Actually Test This (Step-by-Step Setup)
- Set up Burp Suite — intercept all OTP-related requests
- Map out the flow—note down every request that's part of OTP send → verify → success
- Try each method above one by one—don't rush; test methodically
- Document everything — screenshot the request AND response for each test
OTP bypass bugs are some of my favorites to find because the LOGIC is simple—you're literally just asking the server, "Are you SURE you checked this properly?" in 10 different ways.
The beauty of these bugs is that you don't need to know complex coding. You just need to think like a curious person asking, "What if I do this instead?"
Next time you're testing any verification flow—signup, password reset, or 2FA—run through this checklist. You might be surprised how often step 7 (the actual check) is the weakest link in the whole chain.
If this blog helped you understand OTP bugs better, give it a clap and follow for more beginner-friendly security content. Happy ethical hacking! 🔐
This post is part of my ongoing web application security series. Check out my previous posts on SQL injection, XSS, IDOR, and authentication bugs!
Reminder: All these techniques should ONLY be tested on applications you own, or where you have explicit permission (like bug bounty programs). Testing without permission is illegal.