A $10,000 Bug. One JSON Field. Every Account on the Platform.
One intercepted request. One parameter swap. Full access to any account on the platform.Press enter 2026-5-29 09:19:27 Author: infosecwriteups.com(查看原文) 阅读量:20 收藏

LordofHeaven

One intercepted request. One parameter swap. Full access to any account on the platform.

Press enter or click to view image in full size

I was testing a shopping platform for authentication vulnerabilities.

Standard scope. Phone number login, OTP over SMS, the kind of flow that runs on hundreds of apps right now. I entered my number, got my OTP — 8350 — opened Burp Suite, and intercepted the validation request before forwarding it.

I was looking for a bypass. Null OTP, expired token replay, missing rate limit, the classic 000000. I expected to fail ten different ways before finding anything.

Then I looked at the request body:

Press enter or click to view image in full size

almost went straight for the OTP field. Then I stopped.

The phone number was in the request body. Not from a server-side session. Not from a cookie tied to the OTP initiation step. Right there, client-controlled, in the JSON body. The client was telling the server: “this is the number I’m authenticating for.”

I had a real OTP. And if the server was trusting the phone number from the body to decide which account to authenticate into — I didn’t need to bypass anything.

I changed the phone number to a victim’s number. Kept the OTP as 8350. Forwarded the request.

This Is Not What OTP Bypass Looks Like

When people talk about OTP bypass, they mean skipping the OTP entirely. A rate-limiting gap that lets you brute force six digits. A predictable token algorithm. A race condition window. An endpoint that silently accepts a blank value.

Those are bypasses. What I found was not one.

There’s a specific kind of exhaustion that comes with testing auth on platforms that look secure from the outside. You run the obvious attacks, nothing fires, and you start assuming the implementation is solid. That assumption is the most dangerous thing you bring to a test.

OTP authentication creates a strong psychological sense of security — for users and for developers. Two factors: your phone number (your identity) and the code you physically received. The implicit assumption in every OTP flow is that the code only works for the number it was sent to.

That assumption only holds if the server enforces the binding.

If the server trusts the client to report which number is being authenticated, the second factor stops protecting the first. It just adds friction for legitimate users.

What the Request Returned

After swapping the phone number in the body to a victim’s number and forwarding, the server responded

Full JWT tokens. Access token and refresh token. The victim’s phone number echoed back in the response confirming whose account I was now authenticated into.

I opened the platform with those tokens.

The profile page showed a verified account — the victim’s number, their name, their complete order history, saved delivery addresses, and payment methods on file. I had never interacted with that account. I had never received a code for that number. I just told the server which account I wanted.

It gave it to me.

Why the Vulnerability Works — And Why It’s Worse Than It Sounds

The Authentication Logic Was Running Backwards

A correctly implemented OTP flow works like this:

  1. User enters phone number
  2. Server generates OTP, stores it server-side, bound to that specific number
  3. Server sends OTP to that number via SMS
  4. User submits OTP
  5. Server checks: does this OTP match what we sent to the number we have on record for this session?
  6. Match → authenticate the user registered to that number

The critical step is five. The server holds the ground truth: “we sent OTP X to phone Y.” The client can’t touch that. The client can only prove they received it by submitting the correct code. The server validates the code against its own record — not against anything the client sends.

Get LordofHeaven’s stories in your inbox

Join Medium for free to get updates from this writer.

Remember me for faster sign in

This platform had cut that link.

The endpoint was accepting phone from the request body and using it to look up which account to authenticate into. The OTP was being validated — it was a real, unexpired code — but the validation was not bound to a server-side session that tracked which number the OTP was issued for. The server confirmed: "this OTP is valid." Then it authenticated into whatever account matched the phone number the client supplied.

Two separate operations that should have been one inseparable check.

The analogy: A hotel that issues key cards on check-in. At checkout, you hand over any valid key card and say “I’m in room 412.” The front desk verifies the card is authentic. Then they charge room 412. They never checked whether that card was issued for 412 — only that it was a genuine card from their system.

Your card opens room 412. Not because you’re a guest there. Because you said you were.

“But You Still Need a Valid OTP” — Here’s Why That Changes Nothing

The first pushback I expect: an attacker still needs a valid OTP, so doesn’t that limit the attack surface?

No. Here’s exactly why.

A valid OTP requires one thing: owning any phone number registered on the platform. You initiate login with your own number, receive your OTP, intercept the validation request before it fires, swap your number for any victim’s number, forward it.

Your OTP is valid. The server confirms it. You’re now authenticated into the victim’s account.

The attack doesn’t require intercepting the victim’s messages. It doesn’t require access to their device. It doesn’t require their credentials. It requires their phone number — which on a shopping platform surfaces constantly in order confirmation emails, delivery tracking links, referral codes, and support interactions.

The vulnerability wasn’t clever. That was the point — it didn’t need to be.

Every registered user is simultaneously a potential attacker against every other registered user. One attacker, one valid OTP, any target. The scale is limited only by how many phone numbers you can collect — and on a shopping platform, that’s not a meaningful limit.

What Full Access Actually Means on a Shopping Platform

Account takeover on a shopping platform isn’t just a profile page. The authenticated session surfaced:

  • Complete order history — every purchase, amount, date, and delivery address used over time
  • Saved addresses — home, work, frequently used delivery locations with full street details
  • Payment methods — stored cards with last four digits and billing names
  • Active orders — live orders that can be modified, redirected to a different address, or cancelled
  • Loyalty and referral credits — spendable account balance
  • Full PII cluster — name, email, registered phone number in one verified package

On platforms where checkout is one-click with saved payment methods, the access doesn’t stop at data exposure. Depending on the payment confirmation flow — and whether that flow also uses this endpoint — it can extend to transactions.

I tested the authentication flow. I confirmed the takeover. I stopped there.

The Question That Doesn’t Have an Obvious Answer

Authentication security spends most of its attention on hard problems — cryptographic strength, token entropy, algorithm selection, transport security.

None of those were the issue here.

The failure was in trust assignment. The server trusted the client to correctly identify which account was being authenticated for. That trust had no server-side verification. The OTP was valid. The mechanism ran normally. The wrong person got in. And everything looked correct.

If you were on this same test and intercepted this same request — would your first instinct have been to go after the phone number field, or straight to the OTP?

Be honest with yourself before you answer. I went for the OTP first.

Drop your instinct in the comments — I want to know if this is a common blind spot or just mine.


文章来源: https://infosecwriteups.com/a-10-000-bug-one-json-field-every-account-on-the-platform-95471e37f752?source=rss----7b722bfd1b8d---4
如有侵权请联系:admin#unsafe.sh