⚠️ XSS Executed!
Your script ran in the browser!

🎉 Lab Complete!

You executed all three types of XSS attacks!

Next: CSRF Exploitation →
← Back
WAPT / Lab 02 — XSS Attacks
INTERMEDIATE
Tasks

Cross-Site Scripting (XSS) is a vulnerability that lets an attacker inject malicious JavaScript into a web page that other users then view. Unlike SQL Injection which targets the database, XSS targets the browser — the attacker's code runs inside a victim's session, with full access to their cookies, tokens, and page content.

💡
The core idea: A website takes user input and places it inside an HTML page without sanitising it. If that input contains HTML or JavaScript, the browser has no way to know it wasn't put there by the site itself — so it executes it faithfully.
XSS Core Flow
👤 Attacker input
→
⚠️ Reflected in HTML
→
👁️ Victim's browser
→
💀 JS executes

Reflected XSS — the most common type. The malicious script is part of the URL or request. The server reflects it straight back in the response without storing it. It only affects whoever clicks the crafted link.

// Vulnerable server code (PHP) echo "Hello, " . $_GET['name']; // Attacker sends victim this URL: ?name=<script>alert(document.cookie)</script> // Page now contains: Hello, <script>alert(document.cookie)</script>
🎯
Attack vector: The attacker crafts a malicious URL and tricks the victim into clicking it — via phishing email, chat message, or forum post. The script runs in the victim's browser with their session cookies.

Stored XSS (also called Persistent XSS) — the most dangerous type. The payload is saved to the database (e.g. in a comment or profile field) and served to every user who views that page. One injection, unlimited victims.

// Attacker posts a comment: "Great article! <script>stealCookies()</script>" // Server saves it, then renders for all visitors: <div class="comment"> Great article! <script>stealCookies()</script> </div> // Every visitor's browser runs stealCookies()
🚨
Why it's dangerous: Every user who visits the page is compromised — automatically, without any interaction. This can affect thousands of users from a single payload submission.

DOM-based XSS — the server never sees the payload. The vulnerability lives entirely in client-side JavaScript that reads from an unsafe source (like location.hash or document.URL) and writes it into the DOM without sanitising.

// Vulnerable client-side code: const name = location.hash.slice(1); document.getElementById('greeting').innerHTML = name; // Attacker sends victim: page.html#<img src=x onerror=alert(1)> // innerHTML parses it — JS fires!
⚡
Why it's tricky: The server logs show a perfectly normal request — there's no payload in the server response at all. The injection only exists in the browser DOM, making it invisible to server-side WAFs and scanners.

XSS is not just alert(1). In real attacks it is used to steal session cookies (full account takeover), keylog passwords, redirect users to phishing pages, perform actions on behalf of the victim (like changing their email or transferring money), and even spread worm-like payloads that auto-post to infect other users.

🏴
Famous real attacks: The Samy Worm (2005) used Stored XSS to add 1 million MySpace friends in under 20 hours. British Airways (2018) lost 500k customer card details to an XSS-based skimmer. XSS consistently appears in the OWASP Top 10.
❌ Vulnerable
innerHTML = userInput

// Raw HTML injection possible
✅ Safe
textContent = userInput

// Treated as plain text, not HTML

The primary fix is output encoding — converting special characters like <, >, and " into their HTML entities before inserting into the page. Use textContent instead of innerHTML in JavaScript, and use a templating engine that auto-escapes in your backend.

✅
Defence in depth: Output encoding is the core fix. Add a strict Content Security Policy (CSP) to block inline scripts, use HttpOnly cookies so JavaScript can't read session tokens, and validate/sanitise all input on the server.
🎯
Mission: Work through three simulators — Reflected, Stored, and DOM-based XSS. Each one has a vulnerable input. Find the payload that triggers JavaScript execution and collect all three flags!
1
Reflected XSS — trigger an alert
In the Simulator, go to Stage 1: Reflected. The search box reflects your input directly into the page. Craft an input that executes JavaScript — start simple with a script tag or an event handler attribute.
2
Inspect the DOM — see what changed
After a successful payload, look at the Live DOM panel next to the app. Notice how your input was placed raw into the HTML. Can you see exactly where the injection point is and why it worked?
3
Stored XSS — inject into the comment feed
Go to Stage 2: Stored. Post a comment that contains a script. Every "user" who loads the feed will run your code. Notice how the payload persists across sessions — that's what makes this type so dangerous.
4
DOM-based XSS — exploit client-side code
Go to Stage 3: DOM. This time the server isn't involved — vulnerable JavaScript reads from the URL hash and writes it using innerHTML. Craft a payload using an img tag with an onerror handler.
5
Submit all three flags
Each successful XSS reveals a 🚩 flag. The Terminal tab also accepts flag <value>. Collect all three and submit them in the flag box to complete the lab!
💡
Hints guide your thinking — not your clipboard. Try each hint, experiment in the Simulator, then come back for the next one. Real learning happens in the struggle!
When a browser receives HTML, it parses it into a tree of elements. If a <script> tag appears anywhere in that tree, the browser executes its contents as JavaScript — no questions asked.

The key question: if the page puts your input directly inside an HTML element, can you close that element early and start a new one? Think about what characters HTML uses to mark the start and end of a tag.
The simplest XSS payload is just a script tag with JavaScript inside it:

<script>alert(1)</script>

Type that exactly into the search box. Watch the Live DOM panel — does your input appear verbatim in the HTML? If the alert fires, you've confirmed the injection point. Then think: what could you do with document.cookie instead of 1?
In the Stored simulator, your comment is saved and rendered for everyone. The injection trick is the same — get a script tag into the HTML — but now try using an event attribute instead of a script tag:

<img src=x onerror="alert('Stored XSS!')">

This works because src=x always fails to load (no image called "x"), which immediately fires onerror. It's a common bypass when script tags are filtered.
In the DOM simulator, the page's own JavaScript reads from location.hash (the #fragment part of the URL) and writes it with innerHTML.

innerHTML parses whatever string you give it as HTML. So if you put an <img> tag in the hash, the browser will create an actual image element. And if that image has an onerror attribute...

Try typing in the DOM input box: <img src=x onerror=alert(document.domain)>
Each stage reveals its flag when you successfully trigger XSS. The flags appear in the alert overlay AND are logged in the Terminal.

In the Terminal, type flags to see which ones you've collected so far. Once you have all three, paste them together into the flag submission box — or submit each flag one by one using flag WFA{...} in the terminal.
🎬
Video Walkthrough
Watch our full step-by-step solution on YouTube — only after you've given it a proper try!
Watch on YouTube Subscribe — @WhiteFortressSecurity
http://vulnerable-blog.local/search.php
Stage:

📰 VulnerableBlog

// search results page

⚙️ Live DOM — Server Response
Type in the search box to see the HTML...

💬 VulnerableBlog

// comments section

Comments Feed
💬 Recent Comments
Alice
Great article, really helpful!
Bob
Thanks for sharing this tutorial.

👤 VulnerableBlog

// user greeting page

⚠️ VULNERABLE JS (client-side)
const name = location.hash.slice(1);
greeting.innerHTML = name;
⚙️ Live DOM — innerHTML Sink
Type a name to see what innerHTML receives...
🚩 Flag
wfa@lab ~ xss-attacks
White Fortress Academy — Lab Terminal ──────────────────────────── Type 'help' for commands
wfa@lab:~$
🚩 Flag