So I am back at teaching web application security. This time I wanted to setup a CTF challenge for my students. To not reinvent the wheel, or rather, to stand on the shoulders of giants I am reusing the OWASP Juice Shop vulnerable web app in its CTF mode.
Normally I would teach at a (physical) lab which would make the setup easy: all students are situated in the same physical room, I can setup the game server on my laptop and distribute virtual machines containing the vulnerable web app over the local network. Well, that thing called COVID-19 happened so this is not an option right now.
What are my basic requirements?
- a simple game server where teams can register and keep track of their solved challenges. To further facilitate my student’s learning, it would be great if some hints can be displayed within the game server.
- a separate CTF server per team/student containing the vulnerable web application.
- both of them should be hosted “in” the internet. As I do have some Google Cloud credits available, it would be perfect if I can utilize the Google Cloud Platform for this.
- right now, performance is not so much of an issue as I only expect around twenty concurrent students in my lab
Basic Setup/Architecture
After some research I settled on a very simple setup:
- I will use juice-shop-ctf to generate a configuration file for the game server as well as the CTF-key for the vulnerable web applications
- Root-the-Box hosted on a GCP virtual machine as game server
- MultiJuicer on a GCP-hosted Kubernetes cluster to spawn a separate JuiceShop CTF container for each player/team.
Following the recommendation by MultiJuicer I should expect to use 5 CPU-cores as well as around 5 Gigabyte of memory for the Kubernetes cluster for my approx. 10 containers. To make sure we’re on the safe side, let’s double the available memory..
Create CTF Configuration
Initially we need to generate a CTF game-server configuration file: this will contain all data needed for the game-server including the CTF key. This is a shared secret (with the CTF containers containing the vulnerable web sites) that makes sure that only “our” CTF servers are able to commit to “our” game server.
There’s the handy juice-shop-ctf tool to create this configuration file. It’s a node.js program that can be easily be installed using and run using npm (or even be run within the google cloud shell by:
|
|
In this example I have chosen the following configuration (changes):
- use Root-theBox as game-server
- use a new CTF key “trustno1” (please use a more secure one)
- give free text hints to students (within the game server)
- give optional URL hints to students (within the game server). Those are “paid” hints, i.e., if a student chooses to take one it will lower his/her points on the leader board
The command produces a XML-file, this will be imported into the Root-the-Box administrative web-interface later on.
The Game Server: Root-the-Box
Now that we have the configuration covered, the game server is next. The game server will take the configuration file and provide players with an interface to enter their captured flags as well as be able to check the leader-board for their ranking within the CTF game.
I have chosen Root-the-Box as my game engine. In addition to the mentioned features it allows for team communication (chat, shared files), mission trees with optional challenge hints as well as some web-based hacking tools. To keep the setup simple we will deploy it to the Google Cloud Platform itself.
Setting up the game server was pretty straight-forward: go to the Google Cloud Platform -> Computing -> Compute Engine -> VM Instances and create a new Virtual Instance (I have chosen an e2-small image running Debian 10.6). Use the web-based “SSH”-connection to setup the game-server:
|
|
You can close the web-based SSH connection now.
Next we need to make the game server available to the wider internet. To achieve that, go to the created virtual instance and add the network tag “game-server”. Then go to Networks -> VPC Networking -> Firewall and add a new ingress/incoming rule that allows accessing the game-server:
- direction: incoming/ingress
- target/destination: game-server
- source: 0.0.0.0/0
- protocol and port: tcp:8888
With that you can access the game server through http://public-ip:8888. You can gather the public IP from GCP’s virtual instance list. Go to the game server, login with user admin, password “rootthebox”, and set a new admin password. Finally, go to the “Backup/Restore” menu and upload the XML Configuration file that was generated with juiceshop-ctf-cli during the last step. With that, the game-server is now set up for the players (with all the challenges and their corresponding hints).
On a side note: originally I tried to install the game-server in Heroku’s free tier. The setup worked very fine but the free tier does not allow for persistent storage. This means that if the virtual machine is reset or suspended (which happens automatically when it’s inactive) the whole storage is lost. When this happens, the whole setup data is lost and the admin password reset to the default value. Which is not good for a security-related game..
Part 2: Setup multi-juicer in GCP
Now that we have the game server we need to prepare our players’ gaming setup.
Create a new Kubernetes Cluster
Let’s start by creating a new Kubernetes cluster that will hold/spawn the players’ containers.
To do that, go to the GCP and navigate to Computing -> Kubernetes Engine -> Cluster and click the “create new cluster” button. Chose a nearby zone and create the cluster. The default cluster consists of three e2-medium cluster nodes (each containing two vCPUs and four Gigabyte of memory). I’ve added two more, this should be enough to host 10 parallel containers (= teams) while costing around $4-5 per CTF-day.
To setup the CTF container connect through Google Cloud Shell (click on the “connect” button next to the newly created cluster in the cluster view). In the newly connected console check that the cluster is running and deploy the infrastructure for the CTF infrastructure:
|
|
What did I change in the values.yaml:
- change “nodeEnv” from “multi-juicer” to “ctf”
- change “maxInstances” from “10” to “20”
- change “ctfKey” to the key used during running juiceshop-ctf-cli (“trustno1” in our example)
Export the CTF infrastructure to the public internet
Now export the load-balancer (where players can register their teams and spawn their CTF container) to the public internet:
|
|
Uninstallation Instructions
After the CTF event is done, don’t forget to uninstall/remove your setup to prevent run-away Google Cloud Platform costs:
|
|
In addition delete your cluster as well as the virtual instances (cluster nodes and game server).
Appendix: What would I do differently the next time?
This setup works as intended but if I would redo it I’d change the following:
- switch the cluster nodes from e2-medium to e2-small. This is cheaper as base setup, I can always dynamically add more nodes to the cluster anyways — as my containers seem to be mostly CPU-limited this should provide more virtual machines..
- setup SSL certificates and offer everything over HTTPS
- instead of using a separate e2-small instance for the game-server, deploy the game-server into the Kubernetes cluster itself