This article explains a specific XSS vulnerability where user input is placed inside an HTML attribute value and angle brackets are escaped, but the closing quote is not. Because the dangerous character in attribute context is the quote rather than angle brackets, attackers can break out of the attribute and inject event handlers without needing to open new tags. The vulnerability demonstrates why output encoding must be context-aware rather than one-size-fits-all.
- Context determines which characters are dangerousIn HTML body context, angle brackets are the boundary characters that need escaping to prevent tag injection. However, when input lands inside an attribute value, the closing quote becomes the critical boundary. The article demonstrates this through ProfileMe, which escapes < and > but leaves the double quote unescaped, rendering the angle-bracket filtering ineffective in this context.
- Attribute breakout bypasses tag-based defensesAn attacker closes the value attribute with a double quote, then adds a new attribute containing an event handler such as autofocus with an onfocus handler. This approach requires no angle brackets at all and no opening of new tags, making traditional tag-injection defenses irrelevant. The example payload closes the value, injects an event handler pointing to a solve function, and executes when the page loads.
- Why angle-bracket escaping fails hereThe filter in this lab defends against the wrong context's threats. Angle-bracket escaping protects HTML body context where tags are the attack surface, but inside an attribute value, the opening and closing quotes form the actual boundary. Leaving quotes unescaped means the attacker simply terminates the attribute value and writes arbitrary attributes without ever needing to form a tag.
- Context-aware encoding is the correct fixThe article states that HTML-attribute-encoding must escape not just quotes but also ampersands when reflecting input into attribute values. Beyond encoding, the defense includes always quoting attribute values, relying on templating engines that auto-encode per context, and preferring frameworks that escape by default rather than hand-rolled filters. Content-Security-Policy serves as a backstop against injected handlers.
- Common real-world vulnerability patternThe article identifies context-mismatch XSS as a recurring issue where defenses are built for one context but input is reflected into a different one—such as defending body context while reflecting into attributes, URLs, or script contexts. This vulnerability pattern appears frequently enough to be a recognizable cause of 'filtered but still vulnerable' findings in security assessments.
- OWASP classification and prevalenceXSS, including attribute-context variants, is categorized under Injection (A03) in the OWASP Top 10 (2021). The article positions context-aware output encoding as the standard fix within this classification, indicating this is a well-established and widely relevant vulnerability class.
Bottom line: Output encoding must match the exact context where input is reflected; escaping angle brackets in attribute values is insufficient because the closing quote is the actual boundary in that context. Defenders should use context-aware encoding libraries that properly escape quotes and ampersands in attributes, quote all attribute values, and prefer auto-escaping frameworks over hand-rolled filters.
value attribute of an input field, and it escapes angle brackets, so the usual tag-based payloads do nothing. But inside an attribute the dangerous character is the quote that closes the value — and that one is not escaped.📋 In this walkthrough
What this lab teaches
Context decides which characters are dangerous. Reflected inside an HTML attribute value, your input does not need angle brackets to be exploitable — it needs the quote that ends the attribute. Escaping only < and > leaves the attribute context wide open.
The lesson: output encoding must match the context. Angle-bracket escaping is for HTML body context; attribute context needs the quote (and more) encoded too.
How ProfileMe reflects your name
Your ?name= value is placed inside value="…" on an input. Angle brackets are escaped, so you cannot open a new tag. But the double quote that closes the value is reflected as-is, so you can end the attribute and add your own — including an event handler.
The attack approach
- Confirm your name is reflected inside an attribute value and that angle brackets are escaped.
- Realise the quote that closes the attribute is the character that matters here.
- Break out of the value with a quote, then add an event-handler attribute.
- Have that handler call the lab's solve function.
Full walkthrough (spoiler)
If tag-based payloads keep failing, stop trying to open a tag and break the attribute instead — open the steps.
Show the step-by-step solution
- In the name parameter, start by closing the value attribute with a double quote.
- Add an event-handler attribute (such as an autofocus field with an onfocus handler) so no new tag is needed.
- Point the handler at
window.seLabSolve('SE_LAB_XSS_ATTR_2026'). - Load the URL; your input breaks out of the value, the handler runs, and the lab marks you solved.
Why it works
The filter defended the wrong context. It escaped the characters that matter in HTML body text, but your input lands in an attribute, where the closing quote is the boundary. Leave that quote unescaped and the attacker simply ends the value and writes new attributes.
Where this happens in the real world
Context-mismatch XSS — defending body context while reflecting into an attribute, URL or script context — is a common cause of 'filtered but still vulnerable' findings. XSS sits under Injection (A03) in the OWASP Top 10 (2021), and context-aware output encoding is the standard fix.
How to defend against it
- Use context-aware output encoding — attribute-encode in attribute context, including quotes and ampersands.
- Always quote attribute values and rely on a templating engine that auto-encodes per context.
- Prefer frameworks that escape by default rather than hand-rolled filters.
- Add a Content-Security-Policy as a backstop against injected handlers.
Try it, then go further
Reading about it is the easy part. Open the lab, try your own approach before peeking at the spoiler, and see how many attempts it takes.
