Home About Posts Tags Projects Links
How I built a CTF
13 September 2019

I developed a Capture the Flag competition. This is how I did it.

This year actually marked the second CTF I've put together so I had a bit of experience under my belt this time around, but I'm going to try and start from the mindset of knowing nothing about how to make it happen.

It came up after I volunteered to do a talk on CTFs for a local security group. Then it kind of morphed into me building some actual challenges. As time went on I decided to go all out and make it as cool as I could and built more or less a complete competition. This being something completely new to me, there were a few hurdles to jump:

  1. I had no idea where to start
  2. I had no idea how or where to host the challenges
  3. I had no challenges ideas
  4. I'm not an expert in CTFs by any means
  5. I didn't really know the skill levels of everyone in the group
  6. I waited until one month from go live to get started

Where to begin?

Yeah, that last one was a biggie. Luckily I had recently participated in another competition (at least I started on it...) that utilized CTFd just before I was starting to work on my own. It ended up being perfect and exactly what I needed...mostly. It is all containerized and pretty easy to get setup. For this second year I did a little extra and added a feature that maybe I'll talk about in another post. But CTFd saved the day. It's awesome.

Originally I had thought I'd only develop 3 or 4 challenges and maybe we'd cover them all in one meeting. At that stage I thought I'd just bring in my own box with hostapd running on it so players could connect to the hotspot and get to playing. After I decided to get a little more ambitious I thought it would be cool to host it online so players could have more time. I went with Digital Ocean, which ended up being pretty nice and I am a fan now. It's fairly cheap at 5 US dollars per month for the lowest tier machines.

I used nginx (pronounced engine x) as a reverse proxy on the CTFd server so that I could set up SSL on the site with letsencrypt.org. This just means that nginx was configured for encrypted HTTPS traffic and was open to the internet and forwarded traffic to the CTFd Docker container listening on another port internally on the server. The block of code below passes the web server to port 8000, which is where the CTFd Docker container is listening. The rest of the nginx config is pretty standard and I used the "CertBot" from letsencrypt.org to configure SSL. I learned a fair amount from this alone, as I'd never configured HTTPS before.

    location / {
        proxy_pass      http://localhost:8000;

For the challenges...know your audience

Once the server was all stood up it was time to build some challenges...

For the first CTF I was very short on time so I borrowed nearly half of the challenge ideas, and made about half from scratch myself. Even those that I borrowed took quite a bit of work to customize. Some of them worked out really well and were kind of cool (at least I thought so!), others were too difficult or too confusing for the bulk of my players.

For example, I am not great at reverse engineering so I think I borrowed some code from another CTF for that challenge. The problem was that it was way above the skill level of all but a few of my players. As were several other challenges. I didn't do a good job of giving direction and hints. It was a 'beginner' level challenge as things go, but this one was still incredibly difficult and frustrating if you've never done any reversing before.

In my case it was important to remember this was an intro CTF. So this time around I made my challenges easier. I created hints using the CTFd hint system that directed players towards the solution. The first hint for each challenge would generally list tools that may help, and each successive hint gave bigger clues. The goal was to keep people from getting TOO stuck.

If people did get too stuck, I encouraged them to reach out and ask questions. That didn't happen too much so hopefully if there is a next time I can do better to facilitate discussion.

My takeaways

CTFs are fun to play, and they are maybe even more fun to make. I learned PHP, Flask, Docker, honed my Python chops, and many, many other skills. Hopefully I helped a few people learn some things and maybe even get into capturing flags. And perhaps I learned to be a better teacher.

I'm going to clean things up with my write-ups and code then throw them on github, and write some blogs about a few of them.

My challenges are on github here: https://github.com/chadpierce/ctf-challs