RECONOCIMIENTO — Escaneo de puertos
Comenzamos con un escaneo completo de todos los puertos TCP para mapear la superficie de ataque de la máquina víctima:
| PUERTO | ESTADO | SERVICIO | DETALLE |
|---|---|---|---|
| 22/tcp | OPEN | SSH | OpenSSH 9.9p1 Ubuntu |
| 80/tcp | OPEN | HTTP | nginx 1.26.3 → redirige a facts.htb |
| 54321/tcp | OPEN | HTTP | MinIO (S3-compatible object storage) |
Con los puertos identificados, lanzamos un escaneo más profundo con detección de versiones y scripts NSE:
http://facts.htb/ — añadimos el dominio a /etc/hosts. El puerto 54321 es identificado como MinIO, un servidor de almacenamiento de objetos compatible con la API S3 de Amazon. Nmap también detecta que este puerto redirige a http://<IP>:9001, que sería la consola web de administración de MinIO (inaccesible desde el exterior).
ENUMERACIÓN WEB — Camaleon CMS + MinIO
Inspeccionamos el código fuente de la web. Los assets revelan el CMS que hay detrás:
camaleon_first en los assets (tema por defecto de Camaleon CMS), la cookie de sesión con el patrón _<app>_session característico de Ruby on Rails, y la ruta /admin/login que es el panel de administración por defecto de Camaleon. Se trata de Camaleon CMS 2.9.0 sobre Ruby on Rails.
Lanzamos gobuster para enumerar rutas de la web. Entre los resultados, el directorio /admin redirige a /admin/login, confirmando la presencia del panel de administración.
Las imágenes de la web se sirven desde la ruta /randomfacts/. Dado que MinIO está en el puerto 54321, intentamos acceder directamente al bucket randomfacts vía la API S3:
randomfacts es listable y accesible sin autenticación. Además, permite subir ficheros de forma anónima mediante PUT. El bucket actúa como almacén de assets de la web — nginx lo sirve como proxy inverso bajo la ruta /randomfacts/.
CVE-2025-2304 — Privesc en Camaleon CMS → Credenciales S3
Creamos una cuenta de usuario normal en /admin/register. Con una cuenta válida, explotamos CVE-2025-2304, una vulnerabilidad de escalada de privilegios por mass-assignment en Camaleon CMS 2.9.0.
updated_ajax actualiza el usuario con params.require(:password).permit!. El uso de permit! permite todos los campos sin restricción, incluyendo password[role]. Un usuario autenticado puede enviarse a sí mismo el campo role=admin y convertirse en administrador sin necesidad de conocer ninguna credencial privilegiada.
password[password]=test como parte del request de actualización. Si no se especifica --newpass con la contraseña original, el exploit modifica la contraseña del usuario. Hay que añadir --newpass <tu_password> para mantener la contraseña intacta.
Con las credenciales S3 obtenidas del panel de admin, listamos todos los buckets de MinIO:
internal que no era accesible sin autenticación. Contiene lo que parece ser un backup del directorio home de un usuario del sistema.
BUCKET INTERNAL — Clave SSH privada
Exploramos el contenido del bucket internal:
Encontramos una clave SSH privada. La descargamos y le asignamos los permisos correctos:
aes256-ctr). El authorized_keys solo contiene la clave pública sin información del usuario. Los ficheros del bucket tampoco revelan el nombre de usuario del sistema.
CVE-2024-46987 — LFI para obtener el usuario SSH
Para identificar el usuario al que pertenece la clave SSH, usamos CVE-2024-46987, un Path Traversal / Arbitrary File Read autenticado en Camaleon CMS que funciona en la versión 2.9.0 a pesar de estar documentado para versiones anteriores.
download_private_file del MediaController no valida correctamente el parámetro file, permitiendo a un usuario autenticado leer ficheros arbitrarios del sistema mediante path traversal.
trivia y william. La clave SSH pertenece al usuario trivia, que tiene como directorio home /home/trivia. La flag de usuario se encuentra en el home de william.
ACCESO SSH — Crackeo de passphrase con John
Con el usuario identificado, crackeamos la passphrase de la clave SSH usando John the Ripper y el diccionario rockyou:
Con la passphrase obtenida, accedemos al sistema:
La flag de usuario se encuentra en el directorio home de william, accesible con los permisos de trivia:
PRIVILEGE ESCALATION — sudo facter → RCE como root
Comprobamos los privilegios sudo del usuario trivia:
facter es una herramienta de Ruby usada por Puppet para recopilar información del sistema (facts). Permite cargar "custom facts" desde directorios externos mediante --custom-dir. Estos facts son ficheros Ruby que se ejecutan con el intérprete de Ruby de facter — en este caso, como root.
Creamos un custom fact malicioso que añade a trivia a sudoers con permisos totales:
CADENA DE ATAQUE
randomfacts es accesible y escribible de forma anónima.updated_ajax eleva el rol de usuario a admin. El panel de admin expone las credenciales S3 de MinIO en Filesystem Settings.internal, que contiene el directorio home de trivia incluyendo su clave SSH privada cifrada con bcrypt.download_private_file permite leer /etc/passwd y confirmar el usuario trivia. John crackea la passphrase: dragonballz.trivia puede ejecutar /usr/bin/facter como root sin contraseña. Un custom fact Ruby escribe en /etc/sudoers → sudo su → flag de root.