Datos del reto
| Campo | Valor |
|---|---|
| CTF | Batman's Kitchen CTF |
| Reto | MyFirstBlog |
| Categoría | Web |
| Flag | bkctf{k3ys_in_th3_l0ck5} |
Descripción del reto
Era un blog personal bastante simple, pero con dos pistas claras: una entrada "borrada" que seguía siendo accesible y un endpoint de adjuntos que recibía nombres de archivo por parámetro. La resolución terminó saliendo de juntar ambas cosas.
Reconocimiento
En la portada había enlaces a /blog/1, /blog/2 y /blog/4, así que valía la pena probar la ruta que faltaba. En /blog/3 aparecía una entrada marcada como eliminada, pero el HTML todavía incluía información útil.
Ahí se veían dos detalles importantes:
- Un comentario que decía que para los documentos de la carpeta
otherhacía falta una API key. - Un
<object>que cargaba un PDF con esa clave metida directamente en la URL:
/attachment?file=resume.pdf&apiKey=906392d25b3bd7d3af491799f89f6620
Además, el sitio ya usaba /attachment?file=... para servir recursos estáticos, así que el parámetro file era el punto obvio para probar path traversal.
Análisis
Sin la API key, intentar cosas como file=/flag.txt o file=../../../flag.txt devolvía 403. Pero una vez encontrada la clave, el comportamiento cambiaba: el servidor sí permitía acceder a archivos de la carpeta other, y el valor de file no quedaba bien restringido.
Eso abría un path traversal bastante directo. Si el backend resolvía la ruta a partir de other/<archivo> sin limpiar ../, se podía salir del directorio esperado y terminar leyendo /flag.txt.
Resolución
Con la API key expuesta, alcanzaba con pedir el endpoint así:
curl -s "http://34.186.135.240:30000/attachment?file=../../../flag.txt&apiKey=906392d25b3bd7d3af491799f89f6620"
La respuesta devolvía directamente el contenido de la flag.
Flag
bkctf{k3ys_in_th3_l0ck5}