Last updated: 18-March-2018
I recently used the very excellent OWASP Juice Shop application developed by the very excellent Björn Kimminich to run an internal Capture the Flag event (CTF) for my department. It went really well and got really good feedback so I thought I would jot down some practical notes on how I did it.
One important point before you start, you should note the disclaimer that that there are plenty of solutions for this challenge on the Internet.
Someone asked, how did I address that in this case?
(I’ll explain the PDF below)
Anyway, let’s get into the details of how I did the CTF.
RTFM (Read The Full Manual)
First of all, there are some great instructions about how to use Juice Shop in CTF mode in the accompanying ebook, see this section specifically. In this blog post, I want to talk about some of the more specific choices I made on top of those instructions.
Obviously, your mileage will vary but hopefully the information below will help you with some of the practicalities of setting this up in the simplest way possible.
The target applications
I originally thought about getting people to download the docker image onto their own laptops and work on that but in the end I decided to go with the Heroku option from the Juice Shop repository as it appeared that as long as you didn’t hammer the server (which shouldn’t be necessary for this app) then you can host it there for free! (Although you do need to supply them with credit card details just in case).
The only thing to do is make sure you set the correct environmental variable to put the application into CTF mode, see the screenshot below.
I had split my participants into teams (I did this myself to make sure that the teams were balanced) and I set up multiple application instances such that each team was sharing one application instance so that they would see their shared progress but not interfere with other teams. I also made sure each instance had a unique name to stop teams messing with each other.
Spinning up the CTF platform.
I had previously experimented with the CTFd platform when I had first planned this event a year or so ago so I was confident that I would use this as the scoring system and host it myself in an AWS EC2 instance.
When I headed over to their GitHub repository I could see there were a number of different deployment methods and I decided on the “docker-compose” method because I like the simplicity of Docker. Things got a bit messy as I stumbled into a known issue with performance (which has now been fixed) and I also realised that there was no obvious way of using TLS which I decided I wanted as well.
The guys on the CTFd slack channel were really helpful (thanks especially to kchung and nategraf) and eventually I used a fork which nategraf had made which had the performance issue fixed and also had a different version of the “docker-compose” script which included an nginx reverse proxy to manage the TLS termination.
I used an EC2 t2.medium instance for the scoreboard server (mostly because of the original performance problems) but you could probably get away with a much smaller instance. I chose Ubuntu 16.04 as the operating system.
I installed Docker based on the instructions here up to after I had run
apt-get install docker-ce. I then added the local user to the “docker” group using
sudo usermod -aG docker ubuntu (you may need to logout/login after this) and then used the Linux instructions from here to install “docker-compose” (don’t make the mistake I made initially and install via apt-get!)
If you just want to use the scoreboard without TLS then you can just clone the CTFd repository, run
docker-compose up from within the cloned directory, and you are away.
Using TLS with the CTF platform
If you do want a (hopefully) simple way to use TLS, the fork I initially used no longer exists so I have created a deployment repository which uses the same docker compose file that was in the original fork and includes the nginx reverse proxy but will also pull the latest version of CTFd. You can clone that repository from here.
Once you have cloned that, you will need to get yourself a TLS certificate and private key. I used a subdomain of my personal domain (joshcgrossman.com) and the subdomain to the EC2 server’s IP address by adding an A record to my DNS settings. I then used the EFF’s “certbot” which generates certificates using Let’s Encrypt to produce my certificate.
I installed using the instructions here and then while no other web servers were running, I ran the command”
sudo certbot certonly --standalone -d ctfscoreboard.joshcgrossman.com which automatically created the certificate and private key I needed for my chosen domain (make sure port 80 or 443 is open!).
I then renamed the “fullchain” file to ctfd.crt and the “privkey” file to ctfd.key and saved them inside the “ssl” directory which you will have if you cloned my deploy repository above. (The nginx.conf file I used for the TLS version of the deployment looks for these files.)
You then just need to make sure that the hostname in the “docker-compose-production.yml” file matches the hostname of your server (in my case ctfscoreboard.joshcgrossman.com) and you can then run
docker-compose -f docker-compose-production.yml up -d from within your cloned directory and it should start listening on port 443 with your shiny new SSL certificate!
Loading the Juice Shop challenges
Setting the stage
I wanted to provide some brief instructions for the teams and also set some ground rules. For most of them, this was their first CTF and I deliberately made the instructions brief but made myself available to answer questions throughout the CTF. I only had four teams so that was a manageable workload.
I gave the teams the following instructions:
- Each team has their own, Heroku hosted, instance of the vulnerable application. Your scope is limited to that URL, port 443.
- Before the CTF starts, you need to go register your team details in the scoreboard app: https://appteam-ctfscoreboard.joshcgrossman.com (one account per team)
- Once the CTF starts, you can use the “Challenges” screen to enter your flags. You should search for the challenge name on the challenges screen.
- If you miss your flag for some reason, you can go to the scoreboard screen of the vulnerable application and click on the green button to see it again.
- The clock will start at 16:15 and stop at 18:45 at which point you will not be able to record any additional flags.
- Be organised and plan your efforts! (Divide and Conquer!)
I also set down the following ground rules:
- You may not attack or tamper with https://ctfscoreboard.joshcgrossman.com/ in any way whatsoever.
- You may not try and DoS/DDoS your vulnerable application or indeed anything else related to the challenge.
- You may not tamper with another team’s instance, another team’s traffic or anything else related to another team or the organisers.
- You may not use Burp Scanner – it probably won’t help you much and even if it does trigger a flag you won’t understand why it worked.
- You may not search the Internet or ask anyone other than the organisers for anything related to the specific application, the specific challenges or the application’s source code. You may only search for general information about attacks. You have a PDF containing lots of hints about the challenges.
- You may not tamper with the database table related to your challenge progress.
- If you aren’t sure about anything, ask 🙂
- You may have points deducted if you break the rules!
Giving some help
I mention above a PDF with hints. Like I said above, they were not allowed to search the Internet for Juice Shop specific clues but I still wanted them to benefit from hints to help them out. Björn prepared an ebook with all the hints in but it contained the answers as well. In order to save my competitors from temptation, I created a fork with all the answers removed which you can find here.
During the course of the CTF, I projected the CTFd scoreboard onto the big screen and overlaid a countdown timer as well so people knew how long they had to go. I just used a timer from here although it was a little ugly…
I froze the scoreboard for the last 15 minutes to add to the suspense and cranked up some epic music to keep people in the mood.
I’ll leave you with the main guidance I gave to the teams before they started:
- Have fun – that is the main goal of tonight
- Learn stuff – that is the other main goal of tonight
- Don’t get stressed about the time, easy to get overwhelmed
- Team Leaders:
- Divide up tasks
- Decide priorities
- Time management – avoid rabbit-holes
- Escalate questions
- Help those with less experience
Everyone had a great time and I got really good feedback so if you have the opportunity to run something like this, I strongly suggest you take it.
If you have any other questions or feedback let me know, my Twitter handle is above.
Updates: 18-March 2018
Someone asked about team members sharing an instance. I deliberately organised the CTF with teams of 3-4 people. The primary reason was that our department covers a wide spectrum of skill-sets so I still wanted everyone to take part, enjoy and learn something. I therefore carefully balanced the teams based on abilities. (It also meant I could split my direct reports across different teams so no one could accuse me of favouritism 😉)
My logic in a team sharing an instance was to allow progress to be shared and prevent duplicated effort although I think more than four people in a team would not have been manageable. Overall I think that aspect worked well.
Another thought is that if each team member had their own instance, it is more likely that they would all see the solution to each challenge rather than one person completing it and just telling the others. However, this would have slowed things down which in the time we had available probably wouldn’t have been worth it.
One thing I didn’t do beforehand was practice resetting an instance and restoring progress which caused issues when one team created too much stored XSS and another team somehow accidentally changed the admin password without realising it!
Resetting an instance is possible by saving the continue code from the cookie, restarting the instance (that is easy in Heroku) and then sending a post request to the app looking like this:
PUT /rest/continue-code/apply/<<CONTINUECODE>> HTTP/1.1