Session Puzzling Auth Bypass: Variables That Outlive Flows
Session puzzling is an old name for a pattern that refuses to die: the server stores flags like isAuthenticated, userId, mfaPassed, or passwordResetUser in the same session object, and one flow writes a value another flow trusts.
You puzzle the pieces together in the wrong order. Suddenly MFA is "already done."
Map the session writes
Ignore fancy cryptography for an afternoon. Use two browsers and a session viewer—Burp, a logging middleware, or any debug endpoint that echoes session keys (rare, but lovely when present). Walk each auth-adjacent flow and note what gets set:
- Login password step
- MFA / WebAuthn step
- Password reset start and confirm
- Email change verification
- "Remember this device"
- OAuth callback
- Impersonation or support tools
Look for shared keys. If password reset sets uid=123 and the MFA endpoint only checks uid plus otpOk, you may set uid through reset and otpOk through a weaker path.
A concrete puzzle
Example I still see variants of:
- Start password reset for victim@example.com. Session now has
resetUser=victim. - Abandon reset. Start login as attacker, complete password, reach MFA.
- Notice MFA success writes
authed=truewithout clearingresetUser. - Hit an old endpoint like
POST /account/finalize-resetorGET /adminthat checks the wrong combination of flags.
Or the reverse: MFA challenge issued for user A, then you change an email identifier in session via a profile API that does not re-bind the challenge. Submit the code and land in B.
The art is sequencing, not payload encoding. Keep a notebook of session keys after every response.
Race conditions sometimes amplify puzzling. Parallel tabs completing MFA and email change can leave flags from both flows. I still try a deliberate slow step—enter OTP on tab one, change email on tab two, submit OTP—before calling the bug "logic only."
Proving bypass without being a jerk
Use accounts you own. Show a before/after: endpoint denied, then allowed after the puzzled sequence, with session identifiers redacted. Clarify whether you skipped MFA, bound the session to another user, or elevated role.
Name the flags. Vague talk about "session confusion" gets pushback; mfa_ok=true while pending_user still points at another account does not.
Developers fix this by isolating flow-specific session namespaces, clearing flags at transitions, binding challenges to user id + randomness, and treating authentication as an explicit state machine rather than a bag of booleans. Until then, puzzling remains one of the highest-skill, lowest-noise auth tests you can run.
If the program has a dedicated auth bounty tier, lead with the skipped control (MFA, email verify, step-up) rather than the academic name. "Session puzzling" helps other hunters; "MFA bypass via leftover reset flags" helps triage.
Serverless and JWT-heavy apps puzzle less often, but legacy session middleware bolted onto a new MFA widget is fertile ground. Whenever you see a new step-up check talking to an old session store, assume flags collide until the code proves otherwise. A five-minute flag map usually beats another day of payload fuzzing.