Authorization disclaimer. This walkthrough is limited to the intentionally vulnerable PortSwigger Web Security Academy lab linked below. Test only systems you own or are explicitly authorized to assess.

Overview

The login flow verifies a password and then presents a second-factor page, but the account page can be requested directly before the second factor is completed.

The Academy brief provides `wiener:peter` and victim credentials `carlos:montoya`, but states that Carlos's verification code is unavailable.
The Academy brief provides `wiener:peter` and victim credentials `carlos:montoya`, but states that Carlos's verification code is unavailable.

Lab objective/setup

The supplied credentials, candidate lists, email client, and exploit server are used only inside this Academy instance to reproduce the authentication-flow evidence shown below.

What I observed

The password step and the four-digit-code page are separate, but /my-account?id=... does not enforce completion of the second step. After Carlos’s valid password reaches /login2, requesting his account URL directly bypasses the pending code.

The 2FA simple-bypass password step ends on a form requesting a four-digit security code, showing that MFA is still pending.
The 2FA simple-bypass password step ends on a form requesting a four-digit security code, showing that MFA is still pending.
The email client contains Wiener's security-code message, documenting the normal second-factor delivery path.
The email client contains Wiener's security-code message, documenting the normal second-factor delivery path.

Why it is vulnerable

The application treats the first authentication step as sufficient for a protected route instead of binding access to a completed MFA state.

Walkthrough in the authorized PortSwigger lab

Observe the normal Wiener flow through email code and /my-account?id=wiener. Log in with carlos:montoya, stop at the code form, and directly request /my-account?id=carlos.

1
https://LAB/my-account?id=carlos
On `/my-account?id=wiener`, after the code for Wiener is accepted, the My Account page identifies `wiener`, establishing the protected route reached by the normal flow.
On `/my-account?id=wiener`, after the code for Wiener is accepted, the My Account page identifies `wiener`, establishing the protected route reached by the normal flow.
Proxy history shows the successful `/login2` request redirecting to `/my-account?id=wiener`, exposing the directly addressable account route.
Proxy history shows the successful `/login2` request redirecting to `/my-account?id=wiener`, exposing the directly addressable account route.
On `/login2`, after submitting the correct password for Carlos, the browser remains on `/login2` and still requests a four-digit code.
On `/login2`, after submitting the correct password for Carlos, the browser remains on `/login2` and still requests a four-digit code.

Verification

The final capture shows the Academy marking the 2fa simple bypass exercise as solved after the documented request sequence. No testing was performed outside the isolated lab.

Requesting `/my-account?id=carlos` directly displays Carlos's My Account page without submitting his second factor.
Requesting `/my-account?id=carlos` directly displays Carlos's My Account page without submitting his second factor.

Impact and defensive lesson

Authentication controls must bind identity, challenge state, reset tokens, and retry counters to server-side transactions. Return uniform failures, rate-limit by both account and trusted client signals, and use random revocable persistent-session tokens rather than credential-derived values.

References