Facts — HackTheBox Writeup
| Field | Details |
|---|---|
| Machine | Facts |
| OS | Linux |
| Difficulty | Easy |
| Points | 20 |
| Release Date | 2026-01-31 |
| IP | 10.129.17.99 |
Overview
In Facts we exploit two vulnerabilities in Camaleon CMS that allow privilege escalation and local file reading. From there we gain access to a MinIO object storage bucket containing a user’s SSH private key, crack its passphrase, and land a shell. Finally we escalate to root by loading a malicious Ruby script through a privileged facter sudo rule.
Reconnaissance
Nmap
|
|
Three ports are open:
| Port | Service | Version |
|---|---|---|
| 22/TCP | SSH | OpenSSH 9.9p1 (Ubuntu) |
| 80/TCP | HTTP | nginx 1.26.3 |
| 54321/TCP | HTTP | MinIO (Golang net/http) |
The HTTP server redirects to http://facts.htb/ — add it to /etc/hosts.
Web Enumeration
Directory Brute Forcing
|
|
Notable results:
302 GET http://facts.htb/admin => http://facts.htb/admin/login
200 GET http://facts.htb/page
200 GET http://facts.htb/post
200 GET http://facts.htb/robots.txt
Camaleon CMS
The admin login redirects at /admin/login. Response headers confirm the CMS:
link: </assets/camaleon_cms/admin/admin-basic-manifest-...>
After registering a user account the version is visible in the profile panel: Camaleon CMS 2.9.0.
Camaleon CMS
Privilege Escalation via Mass Assignment
Camaleon CMS 2.9.0 is affected by GHSA-rp28-mvq3-wf8j. The password-change endpoint uses Rails’ permit!, which accepts the entire parameter array without filtering:
|
|
The default role for new users is client. The administrator role is admin. By intercepting the password-change request and injecting the role parameter we can promote our own account:
POST /admin/users/5/updated_ajax HTTP/1.1
Host: facts.htb
_method=patch&authenticity_token=<CSRF>
&password[password]=sckull123
&password[password_confirmation]=sckull123
&password[role]=admin
After the request our account has full administrator access to the CMS.
Path Traversal
With admin access we can exploit GHSA-cp65-5m9r-vc2c, which allows reading arbitrary local files through an unsanitised path parameter in the file manager.
Reading /etc/passwd reveals two local users:
trivia:x:1000:1000:,,,:/home/trivia:/bin/bash
william:x:1001:1001:,,,:/home/william:/bin/bash
MinIO — Credential Discovery
Port 54321 runs a MinIO object storage server. Access credentials are visible in the CMS admin storage settings panel.
|
|
Listing the bucket contents reveals what appears to be the trivia user’s home directory synced into the internal bucket:
|
|
The .ssh directory contains both the private key and authorized_keys:
|
|
SSH Private Key Passphrase
The key is passphrase-protected. Extract the hash with ssh2john and crack it with john:
|
|
Shell as trivia
|
|
User flag: 407fc88c5e29ad88876774bb2e2d48ca
Privilege Escalation
Checking sudo permissions:
|
|
/usr/bin/facter is a Ruby script. It accepts a --custom-dir flag that loads and executes every Ruby file in the specified directory:
|
|
We create a Ruby file in a directory we control that copies bash and sets the SUID bit:
|
|
Execute the SUID copy to get a root shell:
|
|
Root flag: 7ab46d7606afb1571fe684300a50c3c0
Attack Chain
[Unauthenticated]
│
▼
Register user on Camaleon CMS 2.9.0
│
▼
Mass Assignment (GHSA-rp28-mvq3-wf8j)
→ Inject role=admin into password-change request
→ Account promoted to CMS administrator
│
▼
Path Traversal (GHSA-cp65-5m9r-vc2c)
→ Read /etc/passwd
→ Enumerate local users: trivia, william
│
▼
MinIO Bucket Enumeration (port 54321)
→ Credentials leaked via CMS admin settings
→ trivia's .ssh directory exposed in bucket
→ Retrieve id_ed25519 private key
│
▼
Crack SSH Key Passphrase
→ ssh2john + john + rockyou.txt
→ Passphrase: dragonballz
→ SSH access as trivia
│
▼
Sudo facter --custom-dir
→ Load arbitrary Ruby from user-controlled directory
→ Copy bash + set SUID bit
→ /usr/bin/sc -p
│
▼
[root]