TJCTF 2023 writeup (Code Review)
Greetings hackers, back again with another CTF which mainly focuses on code review. I've enjoyed solving these challenges with my teammate Abdulhameed Ghazy.
Last updated
Greetings hackers, back again with another CTF which mainly focuses on code review. I've enjoyed solving these challenges with my teammate Abdulhameed Ghazy.
Last updated
The challenge has a description of ezsql
so we know previously what we gonna do. Let's start by trying the web application without the code first :
We can see a simple register form that takes username
and grade
, When I type the username admin
nothing happens like the application refuses this name but when registering with another one :
Now let's check the important code snippets.
Here the code creates the DB and then insert the user admin within it , It also inserts some notes for the admin ... one of this notes contains the flag .
The next part , the code here creates a JWT
token. For the first glance we can think that the challenge is based upon jwt
but the secret here is too long to be brute-forced / guessed
For the last part we can see that the code gets the notes of a user using a sql
query upon his name , here we can notice that there is no sanitization applied to the user input (his name) leaving it vulnerable to SQLi
But not any kind of SQLi
.... Second Order SQLi
, because if we provided our username as follow : guest' or 1=1--
then the query will resolve to : SELECT description FROM notes WHERE owner == 'guest' or 1=1 --;
so it will basically select my notes and any other user notes ( which is the admin ) .
In this challenge we face a python web application which will accept from us python files to run it , so if I have a file named test.py
with the content : print("Hello World")
the result will be:
However if we checked the code we notice that our file should not contain the following:
Not only this , but also:
So we can't use typical open
nor exac, eval
and we cannot use the import
keyword .
These types of challenges usually called Python Jails
so by searching for Escaping Python Jails
I found some good articles and I found this payload : ().__class__.__bases[0]__.__subclasses__()
This will show us python classes which is useful for exploitation, so we could see os
or subprocess
:
So I took it's index and used this payload : ().__class__.__bases__.[0].__subclasses__()[84].load_module("os").system("ls").read()
Now we can easily read the flag❤
Again we know what to do before we start, Let's check the code directly.
The first thing caught my attention was this snippet :
we see that it creates 2 tables : jokes
table and flag
table, but the flag table has random characters appended to it, the flag table has a column called flag
which obviously contains the flag. What about the serialize
word in the beginning? This basically ensures that the statements are executed one by one not in parallel way.
I searched for any other SQL statements and I found this:
The name
parameter is directly passed to the SQL query without any sanitization leaving it vulnerable to SQLi
, to test this I will use burp:
When adding another quote as follow : ?name=''
So this is confirmation of SQLi
, but the main problem here from the last snippet is that the payload is only limited to 6 characters which is very short for our payload.
Checking the code again I saw this line :
Since I don't know what this line means I asked chatGPT:
hmmmm, so this means I can pass the name
parameter as an array ?
This actually worked, Now we can pass name as an array instead of string then this will pass the condition assigned to it:
Now we passed the condition and we can write more than 6 characters we can enumerate for the table name:
This is the flag table, we can then select the flag
column from it and the challenge is solved.
We have in the code three main endpoints 1- Register Endpoint
It starts first by rendering the html template then takes 2 parameters username
& year
2- Retro Endpoint
To access this endpoint we should be logged in first, It also checks for the year
if it was lesser then 1970 then it will render the flag. But the main problem here is that when registering we can see that the minimum year to choose is 1971
3- Login Endpoint
When we login , the code generates a token using generate_token
function.
4- JWT Tokens
the generate_token
function basically generates jwt
token with RS256
algorithm and a private key. When it comes to the verification of the token it uses the public key ( which was included in the challenge files ) and uses list of algorithms not only RS256
as in the generation.
1- Base64 encode the public key.
2- In the jwt editor tab, generate a new symmetric key and update the k
value to be the base64 encoded text.
3- Update the algorithm to be HS256 .
4- Now we can update the token and set the year to be 1969 ( < 1970 ) and then sign the token with the key.
Now let's request /retro
path to get the flag:
And that's it , hope you enjoyed and learned something .... good luck❤
There were many classes but I saw none of which I know, this was new to me. I searched in many blogs until I found this one: which says that I can import modules using _frozen_importlib.BuiltinImporter
class , to my luck it was in the class list:
What we can do here is to force the sever to use the HS256
algorithm instead of the RS256
and use the public key key.pem
as the passphrase .... This kind of attack is called algorithm confusion and you can read about it more . I'm going to use JWT Editor Keys
burp plugin and the steps are as follow: