Skip to content
Field Detail
Platform PortSwigger Web Security Academy
Type Authentication — Password Reset Broken Logic
Difficulty Apprentice
Objective Reset Carlos's password, then log in and access his "My account" page
Note Own credentials wiener:peter, victim username carlos

Password Reset Broken Logic

The lab starts with a "Forgot password" link on the login page. Clicking it redirects to /forgot-password, which prompts for a username or email.

Screenshot

Submitting wiener returned "Please check your email for a reset link." The email delivered a link with a token:

web-security-academy.net/forgot-password?temp-forgot-password-token=dvo8c3biirideeutzmutaf5xk85s256j
Screenshot

A quick sanity check: tampering with the token value directly returned "invalid token," so the token itself is validated server-side.

Screenshot

The interesting part was intercepting the actual POST /forgot-password request after submitting the new password form:

POST /forgot-password?temp-forgot-password-token=17yj7clt07cf9q4ai5azf42v7di8qslh HTTP/2
Host: 0afd001e03bc2136860f7f0800db00bf.web-security-academy.net

temp-forgot-password-token=17yj7clt07cf9q4ai5azf42v7di8qslh&username=wiener&new-password-1=teto&new-password-2=teto

The username parameter was being sent as a separate field in the request body alongside the token — not derived server-side from the token itself. The token proved "this is a valid, in-progress reset flow," but never got bound to a specific user. That meant whoever controls the request controls which account gets its password changed.

Swapping username=wiener for username=carlos while keeping the valid token intact:

POST /forgot-password?temp-forgot-password-token=17yj7clt07cf9q4ai5azf42v7di8qslh HTTP/2
Host: 0afd001e03bc2136860f7f0800db00bf.web-security-academy.net

temp-forgot-password-token=17yj7clt07cf9q4ai5azf42v7di8qslh&username=carlos&new-password-1=teto&new-password-2=teto

The server returned a 302.

Screenshot

Following the redirect gave a 200 OK on /.

Screenshot

The root issue is that anywhere a token is meant to authorize an action against a specific resource, the resource identifier needs to come from the token itself server-side — never from an attacker-controlled parameter in the same request. Worth noting: the token also didn't appear to get invalidated after first use, which would have been a separate finding if the main attack had failed.

Logging in as carlos:teto confirmed the password change went through.

Screenshot

Lab solved

Resources