In the end of this tutorial, you will be able to work with docker files effortlessly.
Intro
When I first started playing CTFs, I used to be given zip files, ie., challenge.zip
.
And in that zip file I kept seeing either Dockerfile
or sometimes both, Dockerfile
and docker-compose.yml
.
Recently, I played a UMDCTF, and there I was given ld-linux-x86-64.so.zip
, a pwn challenge:
hafizfarhad.com@vm:~$ ls
ld-linux-x86-64.so.zip
hafizfarhad.com@vm:~$ unzip ld-linux-x86-64.so.zip
hafizfarhad.com@vm:~$ ls
Dockerfile ld-linux-x86-64.so.2 libc.so.6 Makefile offbyone offbyone.c
hafizfarhad.com@vm:~$
When I unziped it, I could see a Dockerfile
in it.
If you’re new to CTFs like me, these files might seem confusing.
Basically, these files let you recreate the exact same environment that’s running on the challenge server. This means you can test your exploits locally before trying them on the actual challenge server.
Basics
Docker helps you create “containers” - like mini virtual machines that run specific applications. The beauty is that these containers work the same way on everyone’s computer.
In most of the challenges, you’ll typically see one or two main files:
Dockerfile - This file contains Instructions to build a single container
docker-compose.yml - The file contains Instructions to build multiple containers that work together
When to use which file? Always use
docker-compose.yml
file if given.
Commands
When I’m given these files in a CTF, here’s what I typically do:
If you haven’t installed docker, checkout the manual.
For challenges with a docker-compose.yml file:
# Build the containers (the --no-cache makes sure everything is fresh)
docker compose build --no-cache
# Start the containers in the background
docker compose up -d
# See all running containers
docker ps -a
# When you're done, shut everything down
docker compose down -v
Sometimes you will only be given Dockerfile. In that case:
# Build the container
docker build -t challenge-name .
# Run the container (maps port 8080 on your computer to port 80 in the container)
docker run -d -p 8080:80 --name challenge-name-container challenge-name
# Stop the container when done
docker stop challenge-name-container
# Remove the container
docker rm challenge-name-container
Debugging
I’ve encountered various problems and learned useful commands along the way:
Always make sure to add sudo
before commands, otherwise you might get “permission denied” errors.
If a port is already in use, change the port mapping:
docker run -d -p 9090:80 --name challenge-name-container challenge-name
To see container logs:
docker logs challenge-name-container
To find a container’s IP address:
docker inspect challenge-name-container | grep IPAddress
challenge-name
is the Docker image (template), whilechallenge-name-container
is the running instance created from that image. You can create multiple different containers from the same image, each with its own settings.
Cleanup
When you want to cleanup everything, use these commands:
# Remove all Docker images
docker rmi -f $(docker images -q)
# Remove everything - containers, networks, volumes
docker system prune -af --volumes
Wrapping Up
Being able to run challenges locally helps you test ideas quickly without worrying about challenge server timeouts or bans. As you get familiar with these commands, you’ll start to see patterns in how challenges are set up, which can give you hints about where vulnerabilities might be hiding.
Happy hacking!