May 31, 2026
I Typed 000000 as the OTP. The Website Let Me In.
A random phone number. A wrong OTP. Two years, no fix.
LordofHeaven
3 min read
I wasn't trying to find a critical vulnerability that night.
I was registering an account on a platform — call it target.com — and reached the phone number step. Standard habit: I don't use my real number on random platforms. So I typed a fake 10-digit number and submitted.
The OTP went somewhere that wasn't me.
So I typed 000000 and hit submit.
The server returned: 200 OK. {}
Wrong code. But a 200. And an empty body.
I opened Burp Suite. I went back to the beginning.
What OTP Verification Is Actually Supposed to Do
The logic behind phone verification is clean in theory: you can fake any field in a registration form — name, email, country — but you cannot fake receiving an SMS to a phone you don't own.
The OTP is the one thing that's supposed to tie identity to a real device.
The assumption every developer makes when building this: the server verifies the OTP and the result of that verification controls what happens next.
That assumption breaks the moment the result of the verification is communicated back to the client as data — and the client uses that data to decide what to do.
Because anything the server sends to the client can be intercepted. And anything intercepted can be changed before it reaches its destination.
This is response manipulation. It is not a new technique.
What I Did — Step by Step
Step 1 — Register with a phone number I don't own
I filled the registration form: name, email, a random 10-digit number. Target.com accepted it and fired an OTP to that number. Which I don't have.
Step 2 — Capture the OTP verification request
With Burp Suite intercepting traffic, I submitted 000000 as the OTP. The captured POST request contained:
{
"number": "9XXXXXXXXX",
"country": "INDIA",
"code": "000000",
"email": "xyzabc@gmail.com"
}{
"number": "9XXXXXXXXX",
"country": "INDIA",
"code": "000000",
"email": "xyzabc@gmail.com"
}Step 3 — Observe the failure response
The server returned:
HTTP/2 200 OK
Content-Type: application/json
Content-Length: 2
{}HTTP/2 200 OK
Content-Type: application/json
Content-Length: 2
{}A 200 status code. Empty body. That empty {} was the server's failure signal — but it was wrapped in a 200. The client-side logic was not checking the HTTP status code. It was checking the response body for a specific value.
Step 4 — Identify the success response
I created a second account using my real number, submitted the correct OTP, and captured that response:
HTTP/2 200 OK
Content-Type: application/json
{
"content": "Done"
}HTTP/2 200 OK
Content-Type: application/json
{
"content": "Done"
}One field. "content": "Done". That is all the client needed to see to proceed.
Step 5 — Replay with manipulated response
Back to the fake number account. I submitted 000000 again. Burp intercepted the response. I changed {} to {"content":"Done"} and forwarded it.
Target.com took me straight to the account setup screen and onboarding tour.
Full bypass. Fake phone number. Wrong OTP. Zero access to any SMS. Thirty seconds.
Why This Works — The Real Problem
The vulnerability class is client-side trust failure.
Here is the concrete version: the server generates a valid OTP, sends it to the phone number provided, checks whether the submitted code matches, and then communicates the result of that check to the client in a JSON field. The client reads that field and decides whether to proceed.
The problem is that the server never re-validates. Once it sends the response, it trusts whatever the client does next.
Think of it like a locked door with a card reader. The card reader checks your card and says out loud — "valid" or "invalid." But the door opens based on what someone standing inside the room hears, not what the card reader decided. An attacker with a proxy sits between the card reader and the person inside. They change "invalid" to "valid" before it travels.
The door opens.
The fix is not complicated: after a successful OTP verification, the server should issue a short-lived server-side session token that is only generated on a genuine successful check. Every subsequent action in the registration flow — profile setup, onboarding, first login — requires that token. If an attacker manipulates the client-side response, they get a 200 OK back. But they have no valid session token. The server rejects every next request.
The verification result must live in the server's memory of what actually happened, not in the data it sends back.
The VDP Part
The platform had a Vulnerability Disclosure Program. I reported this.
That was in 2024.
As of today, the status is: client still reviewing.
Two years. No fix. No acknowledgment beyond the initial ticket confirmation. The registration flow is unchanged.
I have sent follow-ups. Each one met with the same response: under review.
This is not a complex fix. This is not a business logic flaw buried in a payments system. This is a registration OTP check that does not exist server-side. It would take one developer one afternoon.
Two Years Is Not a Review
Every account created through this bypass has no valid phone number on file. The entire premise of phone verification — fraud prevention, identity binding, account recovery — is defeated for every user registered this way.
A VDP exists to create a formal channel for security researchers to report vulnerabilities without legal risk, in exchange for a reasonable expectation that the vulnerability will be assessed and addressed.
"Under review" for 24 months is not assessment. It is a queue with no exit.
The question I keep returning to: how many VDPs exist primarily to signal that a company takes security seriously, rather than to actually fix what researchers find?
Because if the answer is most of them — that is a problem for every researcher who reports in good faith and waits.
Have you submitted to a VDP that never moved? Drop your timeline in the comments. I want to know if two years is an outlier or the pattern.