CTF Infrastructure

25 May 2020

2 minute read

Capture the Flag challenges are insecure by design, but that doesn't mean that the infrastructure running them should be insecure.

In this post, I'll outline how the challenges and CTF platform were set up in a recent event which SecSoc hosted. If you are a university student interested in security, please check us out!


Our challenges were hosted on a single Google Compute instance as it was going to be a rather small event of around 100 competitors. Some other challenges by external writers were served off our platform, which we didn't have to worry about. All the challenges that we hosted were provided as docker images, which makes the process of deployment much easier.

We also ran an instance of CTFd on a separate VM for security and performance purposes, as it was where the competitors would submit their flags. Again this also ran on docker, using the included docker-compose configuration. CTFd is not very resource intensive, especially for the amount of users that we were expecting (around 100 or so), so it was possible to use a smaller machine (CTFd kinda crashed on us due to a memory leak).

There was no fancy HA setup as it would've been a bit complex to set up. Overall, our Google Cloud bill ended up costing around $60 USD for the whole event (n1-standard-32) for 30 hours due to also running Splunk challenges.

Challenges and Services

Our challenges have a well-defined directory structure which enabled us to parse them with scripts in order to build and push the images to a docker repository, as well as populating our CTFd instance with the challenge prompts.

├── _ctfd
│   ├── challenge.md   # Challenge prompt
│   ├── files          # Files we want to be visible to the player
│   │   └── image.png
│   └── hints          # Markdown flavoured hints
│       └── 1.md
├── src                # Source and/or binaries for our services
└── Dockerfile         # Dockerfile: needs no explanation

For more examples, check out the OWeek repo.

All of the docker challenges are configured inside one docker-compose.yml file as it helps centralise the configuration. By cloning the repo and running docker-compose up -d, we are able to deploy all the challenges at once without having to configure anything, except for DNS (which is also used by Lets Encrypt for certificates).

Additionally, everything is secured under TLS (including the binary challenges), which does not allow competitors to access the challenge without knowing the actual challenge name. A proxy was written so that competitors were able to access the challenges.

More to come...