This Is How I Hacked a Password Reset Flow Without Resetting Passwords

spyboy's avatarPosted by

(Account Takeover via Reset Logic Abuse – Educational Case Study)

Disclaimer
This write-up is strictly for educational and defensive purposes.
All applications, endpoints, tokens, emails, and identifiers are fully anonymized.
No real user accounts were accessed.
The goal is to show how broken reset logic enables account takeover without changing passwords.


Why Password Reset Flows Are a Goldmine

Developers treat password reset as a secondary feature.

Attackers treat it as:

🔑 The primary door into accounts

Because password reset systems:

  • Bypass login entirely
  • Often skip MFA
  • Are rushed features
  • Are rarely threat-modeled

And most importantly:

You don’t need to reset the password to take over the account


Target Overview (Anonymized)

  • Type: Consumer web app
  • Reset method: Email link
  • Flow:
    1. Request reset
    2. Receive reset link
    3. Open link
    4. Set new password
  • Auth: Cookie-based session

Everything looked standard.

That’s what made it dangerous.


Phase 1: Mapping the Reset Flow (Never Skip This)

I requested a password reset.

Network traffic showed:

POST /api/auth/reset/request

Response:

{
  "status": "RESET_SENT",
  "reset_id": "r_9d3f"
}

🚩 Red Flag #1
A reset identifier (reset_id) returned to the client.


Phase 2: Inspecting the Reset Link

The email contained a link like:

https://example-app.com/reset?token=abc123

When opened, the browser sent:

GET /api/auth/reset/verify?token=abc123

Response:

{
  "status": "VALID",
  "reset_id": "r_9d3f"
}

So far, nothing alarming.

But then something important happened.


Phase 3: The Silent Session Creation

After clicking the reset link, the server set a cookie:

Set-Cookie: session=eyJhbGciOi...

😐
No password changed yet.
No confirmation step completed.

The user was already partially authenticated.


Phase 4: Testing What This Session Could Do

I checked basic endpoints:

GET /api/user/profile

Response:

{
  "email": "victim@example.com",
  "plan": "premium"
}

🚨
I was logged in as the victim
➡️ without resetting the password
➡️ without knowing the old password

This wasn’t a reset flow.

This was a login flow in disguise.


Phase 5: Why the Password Never Needed to Change

The backend logic was effectively:

IF reset_token_valid:
    create_session()

Instead of:

IF reset_token_valid AND password_changed:
    create_session()

That missing condition is everything.


Phase 6: Exploiting This Without Email Access

Here’s the truly dangerous part.

The reset token:

  • Was long-lived
  • Could be reused
  • Was not invalidated after verification

That means:

  • Any leaked reset link
  • Browser history
  • Email preview
  • Log exposure

Permanent account access

Even if the victim never resets their password.


Phase 7: Cross-User Reset Abuse (Advanced)

I tested another scenario.

I requested a reset for Account A, got a reset token.

Then I modified the verification request:

GET /api/auth/reset/verify?token=abc123&email=victim@example.com

Response:

{ "status": "VALID" }

🚨
The reset token was not bound to a specific user.

That meant:

One valid reset token could authenticate any account

This is catastrophic.


Real-World Attack Scenarios

This bug enables:

  • 🔓 Full account takeover
  • 📩 No password change alert
  • 🕵️ Silent persistent access
  • 🔑 Bypass MFA entirely
  • 📉 Zero failed login attempts

From the victim’s perspective:

Nothing looks wrong.


Why Monitoring Didn’t Catch It

DefenseWhy It Failed
Login alertsNo login occurred
Password change alertsPassword unchanged
MFANot triggered
Rate limitingValid token
Logs“Reset verification”

This is an invisible takeover.


Root Cause Analysis

❌ Developer Mistakes

  • Treated reset verification as authentication
  • Created session too early
  • Reset token not bound to user
  • Reset token not invalidated
  • No step completion enforcement

This is state confusion, not crypto failure.


✅ How Password Reset Must Work

Non-negotiable rules:

  • Reset token = authorization to change password only
  • No session before password change
  • Token bound to user + action
  • Token single-use
  • Token invalidated immediately after use
  • New login required after reset

Anything else is exploitable.


How I Hunt Reset Flow Bugs (My Exact Playbook)

🔍 Checklist

1️⃣ Does reset link create session?
2️⃣ Can I access APIs after clicking link?
3️⃣ Is password change mandatory?
4️⃣ Can token be reused?
5️⃣ Can token be used for other users?
6️⃣ Does MFA trigger?

If any answer is “yes” — it’s broken.


Tools Used

ToolPurpose
BrowserReset flow
DevToolsSession inspection
Burp SuiteToken manipulation
LogicAlways

Severity Assessment

ImpactLevel
Account takeoverCritical
StealthExtremely high
DetectionNear zero
Abuse scaleHigh

Severity:

Critical – Authentication Bypass


Why This Bug Is So Dangerous

Because:

  • Victims never know
  • Password remains unchanged
  • No alerts are triggered
  • Attacker access persists quietly

This is how long-term breaches happen.


Final Thoughts

You didn’t reset the password.

You reset trust.

And the system gave it to you for free.


Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.