The implication from this tweet is that Web Application Firewalls (WAF) are blocking strings containing the string “burpcollaborator.net” because it is used by Burp Suite when trying to discover vulnerabilities.
As James says in his tweet, there is a trivially simple workaround for this by replacing the “burpcollaborator.net” string with the server’s IP instead although maybe in an effort to keep up with the “arms race”, WAF developers will start to block input containing that IP address as well.
Whilst these sort of protections will not provide any protection against an even slightly motivated attacker, they do involve incurring a time cost to bypass them. Where this becomes an issue is when a client wants us to perform a security test with these protections in place. This is an issue I come across often during application security testing and something that I discuss in my talk, “How to get the best AppSec test of your life“.
Every time a client tells us that they have a WAF in place, I explain that our preferred testing approach is for us to test without being blocked by the WAF and then, and only if absolutely necessary, validate findings against the WAF protected site at the end of the engagement. On a client engagement, we are (usually) being paid to test the client application and not the WAF. We could spent a lot of time and effort specifically trying to bypass the WAF for each attack but that is inefficient for the client.
Another potential issue is that in an attack or other IT incident, a company may be forced to disable their WAF. If their site has not been tested without a WAF, it may therefore still be vulnerable.
Most of the time, clients will accept this approach without issue once the rationale has been explained. Where they don’t accept this, we will usually agree to test anyway but include a disclaimer in the report that the WAF remained active for the duration of the test.
On one memorable occasion, a client decided that I had to verify a finding with their WAF enabled and I had several rounds of cat and mouse with their WAF vendor as I would bypass the WAF and the WAF vendor would deploy a bug fix or a configuration change to address it. This only ended when I sent a payload that crashed the in-line, cloud-based WAF rendering the client’s site inaccessible for several minutes every time I sent the payload. The WAF vendor then claimed victory since “they had blocked the attack”!
Another example is clients where their mobile applications encrypt traffic in transit in addition to the standard TLS encryption. Usually, the client either cannot disable this functionality when we test their applications or are not prepared to do so. Again such a measure incurs a time cost to bypass by either building a tool to perform the decryption and allow us to view/edit the traffic or testing the mobile application and it’s supporting APIs separately.
Either way, clients generally don’t want to pay the cost associated with this but also expect a comprehensive test.
If you are paying for an application security test, you want the money to be spent in the most efficient way possible with the maximum amount of time and effort allocated to testing your application. Making your tester’s life as easy as possible is the best way of achieving that.
If you leave these sort of time wasting security measures in place, you are going to end up spending money testing these measures rather than your application.
If you are really advanced and really confident in your application, you may want to have someone look at vulnerabilities that only occur when your WAF or other security technology is enabled. We have seen examples like this for CDN and caching technologies but if anyone has any WAF specific examples (I am sure I have seen this but cannot remember where), please let me know via Twitter 🙂
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.
UPDATE: For the following section up to the “sudo certbot” below command, I created an ugly shell-script with the commands included.
I installed Docker based on the instructions here up to after I had run sudoapt-get install docker-ce docker-ce-cli containerd.io. 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 from here, run docker-compose up (using -d if you want it to run in the background) 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 an additional docker compose file to configure the nginx instance for TLS. You can clone that repository from here. You will then need to copy/clone the original CTFd repository from here into that same directory. (The ugly shell-script I mentioned does that automatically.)
Once you have cloned that, you will need to get yourself a TLS certificate and private key. I used the EFF’s “certbot” which generates certificates using Let’s Encrypt to produce my certificate. I installed using the instructions here.
If you used my ugly shell-script, this is where it leaves you and you need to continue following instructions.
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.
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.yml -f docker-compose-production.yml up -d from within your cloned directory (or use the run_tls.sh file I supply) and it should start listening on port 443 with your shiny new SSL certificate!
Loading the Juice Shop challenges
This part was easy, I followed the instructions from here to run the tool to export the challenges from Juice Shop and and steps 4 and 5 from here to import the challenges into CTFd.
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.
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
Divide up tasks
Time management – avoid rabbit-holes
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