Skip to content

Reflected XSS — Attribute Injection via Broken HTML Context

Field Value
Platform PortSwigger Web Security Academy
Vulnerability Reflected Cross-Site Scripting (XSS) — Attribute Context
Difficulty Apprentice
Injection Point search query parameter — reflected in value attribute
Goal Break out of the attribute context and inject an event handler

Phase 1 — Identifying the Injection Context

We find the familiar blog with a search bar.

Screenshot

Searching for <marquee>teto</marquee> and inspecting the HTML:

<input type="text" placeholder="Search the blog..." name="search"
       value="&lt;marquee&gt;teto&lt;/marquee&gt;">
Screenshot

The angle brackets < and > are HTML-encoded as &lt; and &gt; — tag injection is blocked. The input is reflected inside a quoted attribute value (value="..."), not as free HTML. A different escape strategy is needed.


Phase 2 — Breaking Out of the Attribute Context

Double quotes are not encoded. Injecting " closes the value attribute early:

<input ... value="" "="">

The value attribute is now closed after the empty string. The stray " creates a broken attribute, which the browser still parses as part of the tag. This is the escape needed to inject new attributes.


Phase 3 — Injecting an Event Handler

Since we can close value="..." and inject content the browser interprets as HTML attributes, we inject a JavaScript event handler:

" onmouseover="alert(0)

The resulting HTML:

<input type="text" placeholder="Search the blog..." name="search"
       value="" onmouseover="alert(0)">
Screenshot

Moving the mouse over the search input triggered the alert. Lab solved.


Why This Works — The Payload Breakdown

<!-- What we sent -->
" onmouseover="alert(0)

<!-- What the browser received and parsed -->
<input value="" onmouseover="alert(0)">
<!--           ↑                       ↑
               Our injected "          The application's original closing "
               closes value=""         completes our onmouseover="alert(0)"  -->

The application's own closing " that it places after the value attribute completes our injected onmouseover="alert(0)" — we don't need to close it ourselves. The surrounding HTML completes the payload.


Conclusion

  1. Angle brackets in the search input were HTML-encoded — tag injection was blocked.
  2. The input was reflected inside a quoted value="" attribute — the injection context was an attribute, not free HTML.
  3. Double quotes were not encoded — injecting " closed the value attribute early.
  4. " onmouseover="alert(0) injected a new event handler attribute on the <input> element; the application's own closing " completed the handler string.
  5. Mousing over the search box triggered the alert.

Key Concepts

The injection context determines the attack technique — different contexts require completely different payloads:

Context Example Payload approach
Free HTML <p>PAYLOAD</p> <script> or <img onerror=...>
Quoted attribute value <input value="PAYLOAD"> " to close attr + event handler
Unquoted attribute value <input value=PAYLOAD> onmouseover=alert(1) directly
JavaScript string var x = 'PAYLOAD' ';alert(1)//

Encoding angle brackets does not prevent XSS if the injection is inside an attribute — angle brackets let you inject tags; double quotes let you inject attributes. Different characters control different contexts. Partial encoding that only blocks < and > while allowing " is insufficient when input is reflected inside an attribute.

Attribute injection uses event handlers instead of script tags — any HTML event (onmouseover, onclick, onfocus, onerror, onload) can execute JavaScript when triggered. When you can't inject <script>, inject an event handler attribute instead.

The application's own surrounding HTML can complete the payload — the closing " placed by the application after value="..." closes our injected onmouseover="alert(0)". Reading the HTML context before crafting the payload reveals what characters you get "for free" from the surrounding markup.