Injection
Log in Join

Lesson 5 · Reading · 30 min

Reflected XSS

Now the interpreter changes from the database to the browser. Cross-site scripting is injection into an HTML page: your input becomes markup, and your <script> runs in a victim's browser, on the victim's origin, with all the trust that origin has. Foundations named it; this lesson exploits its first form, reflected XSS.

The three shapes of XSS: reflected, stored, and DOM-based

The three shapes, and where reflected fits

XSS comes in three shapes, shown above. Reflected XSS is the one where your input travels in the request and is echoed straight back in the response, a search term printed on the results page, an error message that repeats what you typed, a parameter shown in the page. It is not stored anywhere; it lives only in that one response to that one request. That shape decides how it is exploited: because the payload rides in the request, you exploit reflected XSS by getting a victim to send a request you crafted, usually by clicking your link.

Finding the reflection

Step one is to find where your input comes back in the page. Send a unique, harmless marker and search the response for it:

?q=zqxmarker123

View the source (not the rendered page) and find zqxmarker123. Where it landed decides everything:

  • Between tags, as text: <p>You searched for zqxmarker123</p>.
  • Inside an attribute: <input value="zqxmarker123">.
  • Inside a <script> block: var term = "zqxmarker123";.
  • In a URL, an event handler, or a CSS block.

Each context needs a different break-out, so identify it before crafting a payload.

From reflection to execution

Now you test whether the special characters of HTML survive. Send input containing <, >, ", ' and see whether they appear literally in the source or come back encoded as &lt;, &gt;, &quot;. If they are encoded, the app is doing output encoding and the context is (for now) safe. If they appear raw, you can inject markup.

In an HTML text context, the classic proof is to inject a tag that runs script:

<script>alert(document.domain)</script>

alert(document.domain) is the standard, deliberately harmless proof: if a box pops showing the site's own domain, your script executed on that origin. Modern pages often block inline <script> via Content-Security-Policy, so a more reliable proof is an element with an event handler that fires on its own:

<img src=x onerror=alert(document.domain)>

In an attribute context (value="[input]"), you first break out of the attribute and tag before adding your own:

"><img src=x onerror=alert(document.domain)>

The "> closes the existing attribute and tag, then your image and handler follow.

In a script-string context (var term = "[input]"), you break out of the string:

";alert(document.domain);//

The instinct is always the same as SQL injection: understand exactly what surrounds your input, then supply the characters that close it and hand control to you.

Why it matters: it runs as the victim

Stored/reflected XSS runs the attacker's script in the victim's session

When your script runs on the target's origin in the victim's browser, it can do what the victim's own page can: read the DOM, make requests as the victim (their cookies ride along), read a session cookie if it lacks HttpOnly (the link back to the sessions lesson), submit forms, capture keystrokes, or rewrite the page to phish. For reflected XSS the catch is delivery: you need the victim to open your crafted URL, so real impact usually involves a believable link sent to a target. That delivery requirement is why reflected XSS is generally rated below stored XSS, which needs no click.

Content-Security-Policy and honest limits

Many sites ship a Content-Security-Policy that blocks inline scripts, so <script>alert(1)</script> may inject into the markup yet never execute. That matters for your report: an injection that the CSP neutralises is a weaker finding than one that runs. Test whether your payload actually executes (the alert fires), not merely whether it appears in the source. "Self-XSS", a payload only you can trigger on your own account, with no way to deliver it to another user, is likewise low or non-impact. Claim execution and a delivery path, not just reflection.

The honesty gate

Prove execution with a benign payload, alert(document.domain) or a console log, that shows your script ran on the origin. Never deploy a real cookie-stealer against real users, and for reflected XSS demonstrate the crafted URL against your own browser/session as the "victim". The proof is "my script executes on your origin via this link", shown safely.

Your lab

TrailBlaze conditions reflected XSS echoes your input back into the page. Find the reflection, identify the context, get the special characters through, and land a payload that executes. When the alert fires showing the site's domain, you have proven reflected XSS in a realistic app.

Where this goes next

Reflected XSS needs a victim to click your link. The next lesson is the more dangerous sibling: stored XSS, where your payload is saved on the server and served to everyone who views the page, no link, no click, every visitor a victim.

This lesson has 2 labs to try what you just read.