Skip to content
Go back

CTF writeup

Code — Hack The Box Walkthrough

Hack The Box

Code exposed a Python editor on port 5000 and SSH on port 22. The editor accepted Python expressions but blocked strings such as os, exec, read, system, write, and subprocess. Those checks limited obvious payloads, but they did not confine what the interpreter could do.

Find a route through the editor

The service scan identified Gunicorn serving the editor. I tested the filter with invalid code and observed which words it rejected. The breakthrough was constructing blocked names through string concatenation, then using classes already loaded by the Python process. That let me list the application directory and inspect app.py.

The editor eventually let me list the working directory, where app.py stood out. The key result was access to the application’s own Python objects despite the blocked words.

The application source contained a database connection. With the application’s SQLAlchemy model available in the running process, I queried its users and obtained password hashes. A recovered password worked for the martin account over SSH.

The database query recorded in my notes was:

print([{"id": u.id, "username": u.username, "password": u.password} for u in User.query.all()])

Use the backup task

As martin, I found that backy.sh could be run with sudo. It read a JSON task file whose directories_to_archive value I could change. The script attempted to remove ../ from paths, but that string replacement could be bypassed with a path containing ....//: after one replacement pass, traversal components remained.

The task file controlled the script’s archive source and destination. I verified the sudo permission and inspected the script before changing the JSON.

I changed the task to archive a path resolving to /root and wrote the result to /dev/shm:

{
  "directories_to_archive": ["/home/martin/....//....//root"],
  "destination": "/dev/shm"
}

Running the backup script with sudo produced an archive. After extracting it, the listing showed root.txt inside the recovered root directory. This demonstrates access to root-owned files through the privileged backup process. The captured material shows the flag file’s presence, but does not show its contents, so I am not claiming the flag value here.

sudo /usr/bin/backy.sh task.json
cd /dev/shm
tar xjf <generated-backup-archive>.tar.bz2
ls root/
# root.txt appears in the extracted directory

The failure was in the trust boundary around the task file: a user-controlled archive path was processed by a root-run script. Normalizing a path by removing one substring was not enough to ensure it stayed within an allowed directory.