Free Link 🎈
I opened the site just to reset my password. Not hacking. Not hunting. Just forgot it again, like a normal person. Five minutes later, I was still in Burp. Thirty minutes later, I realized I was holding something I definitely shouldn't have.
That's how most real bugs start — not with "let's find a critical," but with "huh… that's weird."
🕵️ Recon: The Boring Part That Always Pays
I wasn't even targeting authentication that day. I was doing mass recon — endpoint mapping, JS scraping, diffing responses, the usual boring grind that nobody posts screenshots of.
While crawling unauthenticated flows, one endpoint kept showing up quietly in responses and JavaScript:
/auth/reset/continueNo UI reference. No documentation. Just… there.
Those endpoints are almost never useless.
🔍 Understanding the Reset Flow (Without Clicking Anything)
Instead of using the UI, I replayed the full password reset flow manually:
- Submit email
- Receive reset token
- Validate token
- Set new password
Nothing unusual at first. But when I intercepted the token validation response, I noticed something off.
The response returned a token like this:
{
"status": "success",
"reset_token": "eyJhbGciOi..."
}I expected this token to be:
- Single-use
- Short-lived
- Tied to the same session
It was none of those.
🔄 Token Reuse: The First Real Crack
I reused the same token:
- In a different browser
- From a logged-out state
- After already resetting the password once
Still valid.
That alone was bad, but I wanted to see how strict the backend really was. So I changed one thing.
I kept the same token, but changed the email parameter.
It worked.
No validation that the token belonged to that email. No cross-check. Just blind trust.
At this point, it was a clear password reset logic flaw.
🧨 Turning One Reset into Many
The password update request looked like this:
POST /auth/reset/continue
{
"email": "victim@example.com",
"reset_token": "VALID_TOKEN",
"new_password": "NewPassword@123"
}There was:
- No nonce
- No token invalidation
- No rate limit
- No binding between user and token
Once you had one valid token, you could reset any account by changing the email.
That alone is critical.
But the app still wasn't done.
🧊 The Cache That Shouldn't Exist
While replaying requests, I noticed this header:
X-Cache: HITOn a password reset endpoint.
I had to recheck. Yes — the reset continuation endpoint was being cached.
After testing more carefully, I confirmed:
- The cache key did not include user-specific headers
- Reset responses containing tokens were cached
- Cached responses were served to other users
That's when this turned from "bad logic" into mass exploitation.
💥 Cache Poisoning Meets Password Reset
By manipulating unkeyed headers (like X-Forwarded-Host) and forcing my reset response into cache, I could poison the response.
Other users requesting the same endpoint would receive my valid reset token.
No phishing. No interaction. No inbox access.
Just passive cache abuse.
This wasn't a single bug anymore — it was a chain:
- Reset token reuse
- Missing user binding
- No invalidation
- Sensitive response caching
- Cache key misconfiguration
Each issue alone was bad. Together, they were devastating.
This allowed:
- Full account takeover
- Any user
- At scale
- Without user interaction
- Using standard requests
This wasn't "reset your own password twice." This was "reset anyone's password if you understand the flow."
Password reset flows are stateful, no matter how REST-like they look.
Connect with Me!
- Gmail: rev30102001@gmail.com