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 persistent-login token is base64(username + ':' + md5(password)). Decoding my own cookie exposed an unsalted password hash, making it possible to calculate candidate cookies for Carlos and test them against GET /my-account.

The Academy brief supplies `wiener:peter`, names Carlos as the target, and requires account access by brute-forcing the persistent-login cookie.
The Academy brief supplies `wiener:peter`, names Carlos as the target, and requires account access by brute-forcing the persistent-login cookie.

Lab objective/setup

Use the supplied account to inspect a legitimate stay-logged-in cookie. The objective is to forge Carlos’s cookie, open his account, and use the lab’s synthetic delete-account action.

What I observed

Selecting Stay logged in produced a long encoded value. Base64 decoding revealed username:hash; comparison with the known password established that the second component was MD5. No server secret or random token contributed to the credential.

The stay-logged-in cookie login form has Wiener's credentials entered and the Stay logged in checkbox selected, the action that causes the persistent cookie to be issued.
The stay-logged-in cookie login form has Wiener's credentials entered and the Stay logged in checkbox selected, the action that causes the persistent cookie to be issued.
Burp history highlights the `stay-logged-in` response cookie set after login, providing the encoded value used for format analysis.
Burp history highlights the `stay-logged-in` response cookie set after login, providing the encoded value used for format analysis.
Base64 decoding the cookie yields `wiener:[md5-digest]`, exposing a username followed by a digest.
Base64 decoding the cookie yields `wiener:[md5-digest]`, exposing a username followed by a digest.

Why it is vulnerable

The server accepts a cookie containing only a public username and a fast, unsalted password digest. Weak passwords can therefore be converted directly into valid persistent-login tokens, while the account endpoint acts as a response oracle for each guess.

Walkthrough in the authorized PortSwigger lab

  1. Log in with Stay logged in enabled, copy the cookie, and decode it to confirm base64(user:md5(password)).
  2. Send GET /my-account to Intruder and mark only the stay-logged-in value as the payload position.
  3. Load the Academy password list. Apply MD5, add the prefix carlos:, and Base64-encode the combined value for every candidate.
  4. Run the attack and identify the response with the distinctive 302 redirect, which indicates that the generated cookie authenticated as Carlos.
  5. Open that response in the browser and use the lab’s delete-account control to complete the objective.
Hash comparison maps the decoded MD5 digest to `peter`, establishing that it is MD5 of the password.
Hash comparison maps the decoded MD5 digest to `peter`, establishing that it is MD5 of the password.
For the stay-logged-in cookie attack, Intruder's payload processors apply MD5, prepend `carlos:`, and Base64-encode the result to recreate the cookie format for each guess.
For the stay-logged-in cookie attack, Intruder's payload processors apply MD5, prepend `carlos:`, and Base64-encode the result to recreate the cookie format for each guess.
The Intruder request targets `GET /my-account` with only the `stay-logged-in` cookie value marked as the payload position.
The Intruder request targets `GET /my-account` with only the `stay-logged-in` cookie value marked as the payload position.
One stay-logged-in cookie Intruder result renders Carlos's My Account HTML and identifies username `carlos`, unlike responses generated by incorrect cookie candidates.
One stay-logged-in cookie Intruder result renders Carlos's My Account HTML and identifies username `carlos`, unlike responses generated by incorrect cookie candidates.

Verification

The decisive Intruder result is the single 302 response among rejected guesses. Following that response opens Carlos’s account, and the Academy solved banner appears after the synthetic account is deleted.

The Academy solved banner appears above Carlos's account after the authenticated delete-account action completes the exercise.
The Academy solved banner appears above Carlos's account after the authenticated delete-account action completes the exercise.

Impact and defensive lesson

Persistent-login cookies should be random, revocable server-side tokens, never encodings of passwords or password hashes. Use modern password hashing for stored credentials, rate-limit authentication attempts, rotate persistent sessions after use, and invalidate them on password changes or logout.

References