0xGodson
Agenda
- What is Dom Clobbering?
- Basics of Dom Clobbering
- Writing Window Object
- Writing Document Object
- Why it’s not possible to Override Window Object and Why it is possible to override document Object
- Javascript Prototype Chain
- Clobbering More that 1 Level
- Conclusion
Prerequisites - HTML, Good Understanding of Javascript and Javascript Prototype
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 attributesidornameare 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. - PortSwigger
Basics of Dom Clobbering
Before getting into Dom Clobbering, we need to learn about id attribute and name attribute
ID Attribute
- The id attribute specifies its element’s unique identifier (ID) . The value must be unique amongst all the IDs in the element’s home subtree and must contain at least one character. The value must not contain any space characters
- Ex:
Name Attribute
- Basically
nameattribute specifies a name for an HTML element. - The name attribute specifies a name for an HTML element. This name attribute can be used to reference the element in a JavaScript. For a <form> element, the name attribute is used as a reference when the data is submitted. For an
<iframe>element, the name attribute can be used to target a form submission. - Ex:
Basic Stuffs
Introduction to Javascript Objects
An object type is simply a collection of properties in the form of name and value pairs. Notice from the list that null and undefined are primitive JavaScript data types, each being a data type containing just one value.
- Ex:
What is Window in Javascript?
The window object is supported by all browsers. It represents the browser’s window. All global JavaScript objects, functions, and variables automatically become members of the window object. Global variables are properties of the window object. Global functions are methods of the window object.
What is document in javascript?
The document object represents your web page. If you want to access any element in an HTML page, you always start with accessing the document object.
- Basically we can Declare Variables in javascript in 3 methods.
var,let,const. Variables Declared withvarare stored under window object with key as variable name and value as declared variable’s value
- We can see,
myNameis a Key in window Object Now.
Writing Window Objects
- Now, We have Basic understanding of
idname,objects. Now Let’s have a Look in Clobbering window Elements. - As we Seen Already, while used
idattribute in HTML tags, we can get those tags bydocument.getElementById("<id>")orwindow.<id>. - For Ex, If we have a HTML tag
<h1 id="hi">Hey!</h1>, then we can get this tag by callingdocument.getElementById("hi")or simply callwindow.hi.
Exploitation:
Hidden toString function call:
- Sometimes in Javascript,when some functions like
toStringare called hiddenly when some functions are called. those functions usetoStringfunction internally for some purpose. Some Examples:
- In the Above Image, I innerHTMLed a Array into the
bodytag. But the Values are Not appended as List, But converted into String.
- Here Again, When Using template String Syntax,
toStringis automatically called! - Another Example,
Array.joincalled automatically with usingArray.toString. There are tons of examples around there.
- Now, How this Hidden
toStringcall ininnerHTMLgoing to help us? - Well, If you found a Reflected HTMLi, then this doesn’t matter. But incase of DOM based HTMLi, then We need the Help of this hidden
toStringcall in order to do something. - Let’s Take a Example:
- Here, I called
toStringfunction for anh1tag which return ->[object HTMLHeadingElement] - So, We can’t actually Declare our own desire value, but still we can create a
keyunder window Object. - A Small Research can help us to find an Interesting behaviour among tags.
var allTags = ["a","img",...]
for (tag of allTags){
temp = `<${tag} id="test">`;
document.body.innerHTML = temp;
try{
if(!document.getElementById("test").toString().includes("[object")){
console.log(tag)}
}catch{}
}- If we Run the About Javascript, We can find that,
atag andareatag doesn’t return[object...whentoStringis called. Something interestingly happing with those 2 tags.
- when we Supply a
hrefattribute with these tags, we can see, the value of thehrefis returned whentoStringis called.
- Pretty Cool. But still we cannot control full value.
protocolis messed up. But still we can usedatauri,javascripturi, other cool protocols likecidand more. - We know, every
idattributes can create akeyin window object. what aboutnameattribute? - Let’s Fuzz Again!
var allTags = ["a","img",...]
for (tag of allTags){
temp = `<${tag} name="test">`;
document.body.innerHTML = temp;
try{
if(window.test){
console.log(tag)}
}catch{}
}- Turns out,
embedformiframeimageimg,andobjecttags can create akeyunderwindowobject. - Basically
documentobject is not likewindowobject that stores variables, functions… - The document object represents your web page. If you want to access any element in an HTML page, you always start with accessing the document object.
- So, How can we clobber
documentobject? Well, Lets Do a Bit of Research.
Writing document Objects
var allTags = ["a","img",...]
for (tag of allTags){
temp = `<${tag} id="test">`;
document.body.innerHTML = temp;
try{
if(document.test){
console.log(tag)}
}catch{}
}- Yeah!, We can Clobber document Object with
objecttag withidas attribute
- Pretty Cool right? Now lets try with
nameattribute
var allTags = ["a","img",...]
for (tag of allTags){
temp = `<${tag} name="test">`;
document.body.innerHTML = temp;
try{
if(document.test){
console.log(tag)}
}catch{}
}- Turns out,
embedformiframeimageimg,andobjecttags can create akeyunderdocumentobject. - Well, in
windowclobbering, we usedatag andareatag withhrefattribute to return our own value. Let See if we can control the output.
var mytags = ["embed","form","iframe","image","img","object"]
for (tag of mytags){
temp = `<${tag} name="test">`;
document.body.innerHTML = temp;
console.log(document.getElementsByName("test").toString())
}Nope, we can’t find anything. So, Its not possible to return our own value.
Why it’s not possible to Override Window Object and Why it is possible to override document Object?
- Maybe you can have a question in your mind. why not overwrite the variables already declared?
- Well, We cannot override/overwrite window
keyswithidattribute ornameattribute. But it is possible to overridedocumentkeys. - So, to understand this answer for this question, you must need good understanding about javascript
prototype chain
Javascript Prototype Chain
- Prototype type chain is little bit huge topic. So i can’t cover that here.
- You can learn that from <a href=“https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain">here</a>. If you kow Tamil, you can refer my OWASP session about Prototype Pollution. There I gave a Detailed View about
prototype chain. you can watch by clicking <a href=“>
youtu.be
slide link: https://github.com/0xGodson/talks/blob/main/prototype%2Bpollution.pdf
youtu.be
- Now, Lets Take this example:
- we have a key
namein the ObjectmyObj. But we Don’t have a keytoStringin the ObjectmyObj. where thetoStringcomes from? It’s from prototype of the Object. As i said before, I am not going to explain this topic. you can learn from the above links. - Cool, In Javascript, there is method/function which help us to find, if the key is really present on the object.
- We can see,
hasOwnProperty("name")returned true andhasOwnProperty("toString")returned false. - So, the
nameis akeyinmyObj,toStringis a function comming from theObject.prototype - what if we mentioned
toStringas akeyinmyObj?
- So, Did we Overwritten the
toStringfunction same fromObject.prototype? No.
- You can see, Still we can access the Original
toStringfunction which printed[object Object].
Edit:
- After some time, <a href=“https://twitter.com/IvarsVids">IvarVids</a> texted that,
myObj.__proto__.toStringis not the correct way of calling,and mentioned to useObject.prototype.toString.call(myObj)instead of this. kudos to <a href=“https://twitter.com/IvarsVids">IvarVids</a> - Why we should call the toString like
Object.prototype.toString.call(myObj)? Well, Before Getting into that We need to know little bit abouttoString. You can completly Skip this if you only interested in Dom Clobbering - Most Common Data types in javascript are
StringArray,Number. - Basically
Object,NumberArrayare build-in Constructor Functions which can be used to CreateObjects,Numbers,Arrays
- The Below Images Shows that, all three Constructor Objects have a property
toStringin its prototype
- This is Tricky one, If you Look Closed to the image,
hasOwnPropertyis coming fromObject.prototype. We already Saw, HowhasOwnPropertyworks. - Even the
hasOwnPropertycoming fromObject.prototypeit is searching for akeytoStringinObject.prototype. - To Understand Better
- Here, We Have a Nested Object.
myobj2is also a Object, so its Prototype is also equal toObject.protoptype - Another Example with Arrays:
- Basically
includesis a property comimg from its prototype [Array.prototype].incluedswill returntrue/falsevalues.trueof passed argument is present in the array, else it will return false. - Even
myarr.__proto__.includesis equal tomyarr.includes, whiles usingmyarr.__proto__.includes(2), theincludesin not searching insidemyarrbut searching insidemyarr.__proto__ - Lets add a value to
Array.prototype
- Now, You can clearly see, how the includes works.
toStringis also works similar to this. Even if we call thetoStringfrom its prototype with__proto__, totoStringis not actually working with themyObjbut withmyObj.__proto__when we callmyObj.__proto__.toString. - When we Call
String.prototype.toString, it will return as aString, If we callArrar.prototype.toString, then it will convert the array tostring. If we call theObject.prototype.toString, then it will return[object <DATA TYPE>].
- So, when we call
myObj.__proto__.toString()we are not actually working withmyObjbut working withmyObj.__proto__.myObj.__proto__.toString()==Object.prototype.toString.call(myObj.__proto__)— - Lets Get Back to the Question…
Why it’s not possible to Override Window Object and Why it is possible to override document Object?
Why we can’t override window clobbering?
- To Understand, Why it is not possible to override
windowobject clobbering, we need to know where and how the elements and variables are stored in the window Object.
- So, if the
h1tagidattributewhoamiis not stored inwindowobject, then how is it possible to accesswindow.whoami? - well, that’s called
prototype chain. While we try to getwindow.whoami, In background, Javascript will follow its prototype chain to find thewhoamikey.
- Now, the
tagswithidare stored into it’s prototype. it is not stored directly in thewindowobject. - We already saw, variables/functions are stored in the
windowobject itself. it is not stored under itsprototype. So, when there is a variable already declared, then we can’t override that. but still we can access throughprototype chain
- while javascript can’t find the
whoamifrom first chain__proto__, then it will automatically move to next chain untill the__proto__hitnull. - This is the Reason why we can’t override
windowclobbering
Why we can override document clobbering?
- we can use any one of the above
tagsfound to be helpful in document clobbering. for now, we are going to useimgtag withnameattribute. - There is a another method in javascript which help us to view the
keysfrom aobject. methods/functions from the prototype will not be returned.Object.getOwnPropertyNames(<Object>)
- Surprisingly, only
location,cookieare it own properties (keys). - So, where the
document.domain,document.getElementById,…. are comming from? yes its from its prototype! - So, If we can add
domainaskeytodocumentobject, when we calldocument.domain, instead of the realdocument.domain, our value will be returned. - So, let now see where and how the values we inject with
imgtag withnameattributes are stored.
- Now, we can see out keys are directly stored into document as it own property.
- So, According to
prototype chain, our tag overrided the orginal property from its prototype. - Except
document.location, we can clobber any thing indocumentObject. This mostly help us to bypass IF Conditions likeif(document.domain != "..."){}and maybe we can break the functionalities by overwriting over used tags! - Example:
Clobbering More that 1 Level
- Till now, We saw dom clobbering level 1. Like clobbering single property. But actually we can clobber multiple levels like a
Object. - What happen if we used same
id2 times for 2 different tags?
- So, basically when we used 2 times same
id, it create aHTMLCollection. We can combineidandnameattributes to build more complexHTMLCollection. - For Example we can use
formtags withinput/outputtags - Why form tags? basically they are nested HTML tags. [Dom Tree Struct]
- we can’t call our own attribute which we created while injecting HTML. This method of Calling out Own
attributesare Exploited in the Past By Legends inIE. But it is not possible now. - Later Terjanq came up a trick which can clobber 4 levels with
iframetag andnameandidattributes with nestediframeswithsrcdocattribute.
- Basically using
nameattribute withaas value creates reference inwindowaswindow.aandsrcdocattribute can be used to injecthtmlcontents. - In the Nested
iframewe usednameattribute and set that tob. So we can access that throughwindow.a.b.Inside the nested frame we using anothersrcdocand we using 2idattributes with same value ascwhich creates aHTMLCollection. - Now, we can access
window.a.b.c, adding anameattribute insideatag, we achive 4 level clobbering .window.a.b.c.d
Conclusion
- If you want to pratice this, you can check my challenge at https://0xgodson.me/my-ctfs/chal1/ and postswigger Labs too!
Fix
- 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.
Resources
https://portswigger.net/research/dom-clobbering-strikes-back
https://portswigger.net/web-security/dom-based/dom-clobbering