Cyber Combat CTF’24 (Web)

Mesum Raza
8 min readJun 18, 2024

--

Assalamualikum everyone. I hope you all are doing well. In today’s writeup I will discuss the solutions of the Web challenges of the Cyber Combat CTF 2024. Cyber Combat CTF was hosted by Rewterz at NED University of Engineering and Technology during the TECH FEST’24 event.

Kudos to

for creating these amazing web challenges for the CTF. If you want to practice the challenges you can checkout this github repository. Let’s get started!!

Challenge 1:

Name: Nova

Solution:

If we open the application and do some recon, there is a login and a register page.

I registered a new user and then logged in to my account and I got the following page:

I quickly checked the cookies and there was a cookie named “session” which was looking like a JWT token. I was pretty sure that this is a Flask JWT session token.

Using the flask-unsign tool, I brute forced the secret and forged a new admin cookie:

Used the new forged admin cookie as the session cookie and I got the flag!!

Challenge 2:

Name: Apex

Solution:

If we open the application, there is an input field in which we can execute our python code.

Let’s check the given source file of this application to see what’s happening behind the scenes.

from flask import Flask, request, jsonify, render_template
import re

app = Flask(__name__)

@app.route('/')
def index():
return render_template('index.html')

@app.route('/run', methods=['POST'])
def run_code():
code_text = request.form.get('code_text', '')
if has_non_ascii(code_text):
return jsonify({"output_text": "Hacking not allowed!"}), 403
result_text = ""
try:
result_text = eval(code_text)
except Exception as e:
result_text = str(e)

return jsonify({"output_text": result_text}), 200

def has_non_ascii(text_string):
ascii_pattern = re.compile(r".*[\x20-\x7E]+.*")
return ascii_pattern.match(text_string)

if __name__ == "__main__":
app.run(host="0.0.0.0", port=5001, debug=False)

So let’s understand what is happening in this code. When we pass our python code to the application, it is passed through the “code_text” parameter. This parameter is then passed to a function “has_non_ascii”. In this function there is a regex which checks if the given input has ascii characters with “ascii_pattern.match” and if there are ascii characters in the input it displays a message “Hacking not allowed!” and if there are no ascii characters then our input is passed into the eval() function.

So now after reading the code I got an idea that I have to bypass the regex and then my input will be passed to eval() function which is a vulnerable function that allows arbitrary code execution.

After some research I found this article which explained how we can bypass the regex in python with a line feed (\n) if our input is checked through “re.match”.

def has_non_ascii(text_string):
ascii_pattern = re.compile(r".*[\x20-\x7E]+.*")
return ascii_pattern.match(text_string)

So basically in this code “ascii_pattern.match” is used to check the regex. In python “.match” only checks the beginning of the string not the beginning of each line means if we enter a new line in our input, only the first line will be checked against the regex and another line will not be checked and will be directly passed in the input.

Time to exploit it, I searched for python eval exploit and this article helped me achieve my goal. I used burp decoder to make my final payload.

Notice the extra line in the first block which is basically a new line to bypass the regex.

I quickly set up my ngrok and the netcat listener, copied the payload and passed in the input field:

And boom we got the reverse shell:

And I got the flag!!

Challenge 3:

Name: Nexus

Solution:

If we open the application and do some recon, there is a login and a register page:

I registered a new user and then logged in to my account and I got the following page:

I checked the cookies and this time I got a cookie named “JWT” and this is a JWT token:

I brute forced the JWT token signing key and I got the secret sigining key:

Note that because I have already brute forced this token that’s why I am using “show” flag to directly get the secret.

Moving forward I forged a new admin token from https://jwt.io/ to see if I get something interesting but no I got the same results just with a changed username:

At first I was not able to understand what to do next but then I noticed that username is reflected on the page maybe there can be a SSTI. I used the basic payload “{{7*7}}” and forged a new JWT cookie:

And yes I was correct, it returned the following output:

I then checked if Jinja2 template is used at the backend with “{{7*’7'}}” and it returned 7777777 which confirmed that Jinja2 is used.

I have already explained Jinja2 detection in my previous writeup you can check it there.

Now moving forward I went to PayloadAllTheThings SSTI cheatsheet and copied the payload to read the flag but the main twist started here. When I forged my new cookie with the SSTI payload to read the file I got the following message:

Note that the SSTI payloads can’t be directly used in JWT payload section, it will give error because we have to escape some characters. I have used chatGPT to escape the characters.

I tried a lot of different payloads but everytime I was getting blocked becuase there are black-list filters in place which doesn’t allow me to use certain words and characters. After a lot of trials I found the following filter bypass payload from the PayloadAllTheThings cheatsheet which bypassed all the major filters (‘.’,’_’,’|join’,’[‘,’]’,’mro’ and ‘base’):

{{request|attr('application')|attr('\x5f\x5fglobals\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fbuiltins\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fimport\x5f\x5f')('os')|attr('popen')('id')|attr('read')()}}

But in this payload, the word application, os and popen were not allowed. When I saw that hex encoding is used in the above payload and is allowed by the challenge, I encoded the words application, os, popen to hex.

My final payload after converting the words to hex and escaping the characters with CHATGPT was:

{{request|attr('\\x61\\x70\\x70\\x6c\\x69\\x63\\x61\\x74\\x69\\x6f\\x6e')|attr('\\x5f\\x5fglobals\\x5f\\x5f')|attr('\\x5f\\x5fgetitem\\x5f\\x5f')('\\x5f\\x5fbuiltins\\x5f\\x5f')|attr('\\x5f\\x5fgetitem\\x5f\\x5f')('\\x5f\\x5fimport\\x5f\\x5f')('\\x6f\\x73')|attr('\\x70\\x6f\\x70\\x65\\x6e')('ls ../')|attr('read')()}}

I forged the new JWT token:

And finally I got the results:

And wait there is one more twist left, the word “flag” is also not allowed so for that I used backtiks to concatenate the string together like fl``ag.txt. Final payload that I used:

{{request|attr('\\x61\\x70\\x70\\x6c\\x69\\x63\\x61\\x74\\x69\\x6f\\x6e')|attr('\\x5f\\x5fglobals\\x5f\\x5f')|attr('\\x5f\\x5fgetitem\\x5f\\x5f')('\\x5f\\x5fbuiltins\\x5f\\x5f')|attr('\\x5f\\x5fgetitem\\x5f\\x5f')('\\x5f\\x5fimport\\x5f\\x5f')('\\x6f\\x73')|attr('\\x70\\x6f\\x70\\x65\\x6e')('cat ../fl``ag.txt')|attr('read')()}}

And I finally got the flag!!

Challenge 4:

Name: Onion

Solution:

If we open the application and do some recon, there is an option to upload a pickle file and it will unpickle it for us.

After doing some research I got to know that “pickle” is basically a python module which lets you serialize and unserialize the data. So according to this application we will provide the serialized data pickle file and it will unserialize it for us.

After further research I found this article which explained pickle module insecure deserialization vulnerability. I followed the article and used chatGPT to make my exploit. My final exploit code was:

So let us understand what is happening in the above code:

  1. First we have imported pickle and os module for our use.
  2. Then we have defined a class RCE in which we have defined __reduce__ method. In the above mentioned article, the researcher has explained really well why are we using reduce method here. Just to directly tell that from this method we will run our reverse shell code.
  3. After that I have run pickle method on the RCE class which will serialize the contents of the class and it is stored in “pickle”.
  4. Then a file named “exploit.pickle” is created in the write binary (wb) mode and the contents of pickle are stored in it.

Now its time to exploit, I ran my python script with:

> python3 test.py

And a file named exploit.pickle was created.

I quickly set up my ngrok and netcat listener and then uploaded the exploit.pickle file to the application and we got the shell:

And I got the flag!!

I hope you enjoyed the writeup and learnt something new. Meet you guys in the next writeup. Till that peace out!!

Feel free to connect!

LinkedIn: https://www.linkedin.com/in/mesum-raza-471651234/

--

--

Mesum Raza

Security Researcher | CAP | ISO/IEC 27001 | BS Cyber Security | NED'27 | Offensive Security