DOM-based XSS
🧠 ¿Qué es DOM-based XSS?
Ocurre cuando:
-
La entrada del usuario NO llega al servidor
-
El JavaScript del navegador procesa la entrada
-
El DOM se modifica dinámicamente
-
El código malicioso se ejecuta en el cliente
📌 Diferencia clave:
| Tipo XSS | Procesamiento |
|---|---|
| Stored | Servidor + BD |
| Reflected | Servidor |
| DOM-based | 🧠 Navegador (JavaScript) |
🧪 Entorno vulnerable de práctica
Aplicación web tipo To-Do List, visualmente igual a las anteriores.
Accedemos a:
http://<IP>:<PUERTO>/
Probamos a añadir un texto normal:
test
🖥️ Resultado:

- Se muestra:
Next Task: test
🌐 Análisis clave: pestaña Network
Abrimos las DevTools:
CTRL + Shift + I → Network
Repetimos la acción (Add) y observamos:
❌ NO se genera ninguna petición HTTP

📌 Esto indica que:
-
El input no se envía al servidor
-
Se procesa solo con JavaScript
-
Estamos ante un DOM-based XSS
🔗 Parámetro en la URL (#)
Observamos que la URL cambia así:
http://<IP>:<PUERTO>/#task=test
#?
-
Todo lo que va después de
#:-
❌ No se envía al servidor
-
✅ Solo lo lee el navegador
-
-
Muy común en:
-
SPA
-
Aplicaciones JS modernas
-
📄 ¿Por qué no aparece en el código fuente?
Si miramos el código fuente con:
CTRL + U
📌 No veremos nuestro input.

-
El HTML base se carga primero
-
Luego JavaScript modifica el DOM
Por eso:
-
No persiste
-
No aparece en el source original
Para verlo correctamente:
CTRL + SHIFT + C
➡️ HTML renderizado dinámicamente
🧩 Concepto clave: Source & Sink
Si controlas la Source y llega a un Sink inseguro, hay XSS.
🔹 Source (origen de la entrada)
Es de dónde sale el dato que controla el usuario.
📌 Ejemplos comunes de Source:
-
Parámetros de la URL
-
location.href -
location.hash -
document.URL -
Campos de formularios
-
Cookies
📎 En este caso, la Source es el parámetro task:
var pos = document.URL.indexOf("task=");
var task = document.URL.substring(pos + 5, document.URL.length);
El atacante controla completamente el valor de task.
🔻 Sink (destino peligroso)
Es la función que escribe la entrada del usuario en el DOM.
📌 Si el Sink no sanitiza, ejecutará código malicioso.
Funciones JavaScript peligrosas
-
document.write()
-
element.innerHTML
-
element.outerHTML
Funciones jQuery peligrosas
-
add()
-
after()
-
append()
📎 En este caso:
document.getElementById("todo").innerHTML =
"<b>Next Task:</b> " + decodeURIComponent(task);
Source controlable + Sink sin sanitización = DOM XSS
🧨 Ataque DOM XSS
❌ Payload clásico que NO funciona
<script>alert(window.origin)</script>
-
innerHTMLbloquea<script> -
Es una medida de seguridad del navegador
✅ Payload funcional (sin <script>)
<img src="" onerror=alert(window.origin)>
<img src="" onerror=alert(documento.cookie)>

-
Se crea una imagen
-
src=""falla -
Se dispara
onerror -
Ejecuta JavaScript
🔗 URL maliciosa final
El payload se pasa directamente en la URL:
http://<IP>:<PUERTO>/#task=<img src="" onerror=alert(window.origin)>
📸 Resultado:
-
Popup con el dominio vulnerable
-
Código ejecutado
-
❌ El servidor no recibe nada
🎯 Explotación real
📌 Envío de enlaces maliciosos vía:
-
Phishing
-
Chats
-
Emails
-
Redes sociales
-
QR maliciosos
👉 Cuando la víctima visita la URL:
-
El navegador ejecuta el payload
-
El servidor no ve nada
🧠 Comparación rápida de los 3 XSS
| Tipo | Persistente | Servidor | Cliente |
|---|---|---|---|
| Stored XSS | ✅ | ✅ | ✅ |
| Reflected XSS | ❌ | ✅ | ✅ |
| DOM-based XSS | ❌ | ❌ | ✅ |
🧠 Resumen final
-
No llega al servidor
-
Totalmente del lado cliente
-
Basado en Source & Sink
-
Muy común en apps JS modernas
-
Difícil de detectar desde backend
-
Ideal para phishing y ataques dirigidos