June 21, 2026
Breaking Out of the Attribute: My Seventh XSS Lab on PortSwigger
I’ve been working through the PortSwigger Web Security Academy XSS labs one by one, and this seventh lab introduced a twist I hadn’t…
Diya
4 min read
I've been working through the PortSwigger Web Security Academy XSS labs one by one, and this seventh lab introduced a twist I hadn't encountered yet. Angle brackets are encoded, so injecting a new HTML tag isn't possible. But the input is still reflected inside an attribute value, which opens a different kind of attack. Here's how it went.
Category: Reflected Cross-Site Scripting (XSS)
Difficulty: Apprentice
Lab Link: Reflected XSS into attribute with angle brackets HTML-encoded
Objective
Perform a reflected XSS attack that injects an attribute into the search input and calls the alert function.
Vulnerability Overview
This lab demonstrates a reflected XSS vulnerability where the application encodes angle brackets (< and >), preventing direct HTML tag injection. However, the search input is reflected inside an HTML attribute value, specifically the value attribute of the search <input> element, without encoding double quotes.
This means that while a payload like <script>alert(1)</script> would be rendered harmless (the angle brackets get encoded to < and >), a payload that breaks out of the attribute context using a double quote can still inject a new event handler attribute directly into the existing tag.
Steps to Reproduce
- Opening the lab reveals a blog home page with a search box. The search functionality reflects user input back onto the page.
- Submitting a test input (
abc123) and inspecting the page source via DevTools (Sources tab) reveals the reflection point at line 55:
<input type=text placeholder='Search the blog...' name=search value="abc123"><input type=text placeholder='Search the blog...' name=search value="abc123">The input is reflected inside the value attribute of the <input> tag, wrapped in double quotes. This is the injection point.
Checking the Elements tab confirms the same. The value="abc123" is visible in the rendered DOM, and angle brackets in any test input would be HTML-encoded.
-
Since angle brackets are encoded but double quotes are not, the payload works by:
-
Closing the existing
valueattribute with a " -
Injecting a new
onmouseoverevent handler attribute -
Leaving the rest of the tag intact
"onmouseover="alert(1)"onmouseover="alert(1)This transforms the rendered HTML into:
<input type=text placeholder='Search the blog...' name=search value="" onmouseover="alert(1)"><input type=text placeholder='Search the blog...' name=search value="" onmouseover="alert(1)">Enter the payload into the search box and click Search.
- After submitting the payload, hover the mouse over the search input field. The
onmouseoverevent fires andalert(1)executes, confirming the attribute injection was successful.
The DOM at this point (visible in Elements tab) confirms the injected attribute:
<input type="text" placeholder="Search the blog..." name="search" value="" onmouseover="alert(1)"><input type="text" placeholder="Search the blog..." name="search" value="" onmouseover="alert(1)">
The lab is marked as solved as soon as the alert fires.
Technical Evidence
The key difference between this lab and the earlier reflected XSS labs is the injection context. In the first lab, the payload was reflected in the HTML body, so injecting <script> tags worked directly. Here, the reflection is inside an attribute value, and angle brackets are encoded:
If a payload like <script>alert(1)</script> is submitted, it appears in the source as:
value="<script>alert(1)</script>"value="<script>alert(1)</script>"The encoding neutralizes the angle brackets, preventing tag injection. But double quotes are not encoded, so breaking out of the attribute context is still possible. The injected payload results in:
<input type=text placeholder='Search the blog...' name=search value="" onmouseover="alert(1)"><input type=text placeholder='Search the blog...' name=search value="" onmouseover="alert(1)">The browser parses this as a valid <input> element with an onmouseover handler, and executes it when the user hovers over the element.
Root Cause
The application applies output encoding to angle brackets but fails to encode double quotes when reflecting user input into an HTML attribute context. This partial encoding is insufficient. Proper context-aware output encoding requires encoding all characters that are meaningful in the current context. Inside a double-quoted attribute value, the double quote character itself must be encoded as " to prevent attribute breakout.
Impact
An attacker could craft a malicious URL containing the payload in the search parameter and trick a victim into visiting it. When the victim hovers over the search input (which is prominently placed on the page), the injected event handler fires. Potential consequences include:
- Session hijacking via cookie theft
- Performing actions on behalf of the victim within the application
- Redirecting the victim to a phishing or malware-distributing site
- Capturing keystrokes or other sensitive input from the page
The onmouseover trigger does require the victim to interact with the page (by hovering), but this is easily achieved since the search box is a natural focal point of the page.
Remediation
To prevent this type of vulnerability, the application should:
- Apply context-aware output encoding. When reflecting user input inside an HTML attribute value, encode all special characters meaningful in that context, including double quotes (" →
"), single quotes (' →'), and angle brackets. - Use templating engines with automatic escaping (e.g., Jinja2 autoescape, React's JSX) instead of manually constructing HTML strings, as they handle context-aware encoding by default.
- Validate and sanitize input on the server side, rejecting or stripping characters that have no legitimate use in the expected input (e.g., quotes in a search query).
- Implement a Content Security Policy (CSP) to restrict inline event handler execution as a defense-in-depth measure.
Conclusion
This lab was a good reminder that "some encoding" is not the same as "safe." The application encoded angle brackets, which would have stopped a naive <script> injection but left double quotes unencoded, which was enough to break out of the attribute context entirely and inject an event handler. The attack surface shifted from the HTML body to the attribute context, and the payload had to adapt accordingly. Seven labs in, and the theme is consistent: the injection context determines the payload, and partial mitigations can create a false sense of security.