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 attributesid
orname
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. - 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
name
attribute 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 withvar
are stored under window object with key as variable name and value as declared variable’s value
- We can see,
myName
is a Key in window Object Now.
Writing Window Objects
- Now, We have Basic understanding of
id
name
,objects
. Now Let’s have a Look in Clobbering window Elements. - As we Seen Already, while used
id
attribute 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
toString
are called hiddenly when some functions are called. those functions usetoString
function internally for some purpose. Some Examples:
- In the Above Image, I innerHTMLed a Array into the
body
tag. But the Values are Not appended as List, But converted into String.
- Here Again, When Using template String Syntax,
toString
is automatically called! - Another Example,
Array.join
called automatically with usingArray.toString
. There are tons of examples around there.
- Now, How this Hidden
toString
call ininnerHTML
going 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
toString
call in order to do something. - Let’s Take a Example:
- Here, I called
toString
function for anh1
tag which return ->[object HTMLHeadingElement]
- So, We can’t actually Declare our own desire value, but still we can create a
key
under 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,
a
tag andarea
tag doesn’t return[object...
whentoString
is called. Something interestingly happing with those 2 tags.
- when we Supply a
href
attribute with these tags, we can see, the value of thehref
is returned whentoString
is called.
- Pretty Cool. But still we cannot control full value.
protocol
is messed up. But still we can usedata
uri,javascript
uri, other cool protocols likecid
and more. - We know, every
id
attributes can create akey
in window object. what aboutname
attribute? - 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,
embed
form
iframe
image
img
,andobject
tags can create akey
underwindow
object. - Basically
document
object is not likewindow
object 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
document
object? 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
object
tag withid
as attribute
- Pretty Cool right? Now lets try with
name
attribute
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,
embed
form
iframe
image
img
,andobject
tags can create akey
underdocument
object. - Well, in
window
clobbering, we useda
tag andarea
tag withhref
attribute 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
keys
withid
attribute orname
attribute. But it is possible to overridedocument
keys. - 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=“>
- Now, Lets Take this example:
- we have a key
name
in the ObjectmyObj
. But we Don’t have a keytoString
in the ObjectmyObj
. where thetoString
comes 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
name
is akey
inmyObj
,toString
is a function comming from theObject.prototype
- what if we mentioned
toString
as akey
inmyObj
?
- So, Did we Overwritten the
toString
function same fromObject.prototype
? No.
- You can see, Still we can access the Original
toString
function which printed[object Object]
.
Edit:
- After some time, <a href=“https://twitter.com/IvarsVids">IvarVids</a> texted that,
myObj.__proto__.toString
is 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
String
Array
,Number
. - Basically
Object
,Number
Array
are build-in Constructor Functions which can be used to CreateObjects
,Numbers
,Arrays
- The Below Images Shows that, all three Constructor Objects have a property
toString
in its prototype
- This is Tricky one, If you Look Closed to the image,
hasOwnProperty
is coming fromObject.prototype
. We already Saw, HowhasOwnProperty
works. - Even the
hasOwnProperty
coming fromObject.prototype
it is searching for akey
toString
inObject.prototype
. - To Understand Better
- Here, We Have a Nested Object.
myobj2
is also a Object, so its Prototype is also equal toObject.protoptype
- Another Example with Arrays:
- Basically
includes
is a property comimg from its prototype [Array.prototype
].inclueds
will returntrue/false
values.true
of passed argument is present in the array, else it will return false. - Even
myarr.__proto__.includes
is equal tomyarr.includes
, whiles usingmyarr.__proto__.includes(2)
, theincludes
in not searching insidemyarr
but searching insidemyarr.__proto__
- Lets add a value to
Array.prototype
- Now, You can clearly see, how the includes works.
toString
is also works similar to this. Even if we call thetoString
from its prototype with__proto__
, totoString
is not actually working with themyObj
but 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 withmyObj
but 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
window
object clobbering, we need to know where and how the elements and variables are stored in the window Object.
- So, if the
h1
tagid
attributewhoami
is not stored inwindow
object, 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 thewhoami
key.
- Now, the
tags
withid
are stored into it’s prototype. it is not stored directly in thewindow
object. - We already saw, variables/functions are stored in the
window
object 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
whoami
from first chain__proto__
, then it will automatically move to next chain untill the__proto__
hitnull
. - This is the Reason why we can’t override
window
clobbering
Why we can override document clobbering?
- we can use any one of the above
tags
found to be helpful in document clobbering. for now, we are going to useimg
tag withname
attribute. - There is a another method in javascript which help us to view the
keys
from aobject
. methods/functions from the prototype will not be returned.Object.getOwnPropertyNames(<Object>)
- Surprisingly, only
location
,cookie
are it own properties (keys). - So, where the
document.domain
,document.getElementById
,…. are comming from? yes its from its prototype! - So, If we can add
domain
askey
todocument
object, 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
img
tag withname
attributes 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 indocument
Object. 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
id
2 times for 2 different tags?
- So, basically when we used 2 times same
id
, it create aHTMLCollection
. We can combineid
andname
attributes to build more complexHTMLCollection
. - For Example we can use
form
tags withinput/output
tags - 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
attributes
are 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
iframe
tag andname
andid
attributes with nestediframes
withsrcdoc
attribute.
- Basically using
name
attribute witha
as value creates reference inwindow
aswindow.a
andsrcdoc
attribute can be used to injecthtml
contents. - In the Nested
iframe
we usedname
attribute and set that tob
. So we can access that throughwindow.a.b
.Inside the nested frame we using anothersrcdoc
and we using 2id
attributes with same value asc
which creates aHTMLCollection
. - Now, we can access
window.a.b.c
, adding aname
attribute insidea
tag, 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