# TJCTF 2023 writeup (Code Review)

## Web / Swill-Squill

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 : <br>

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FRoNAsPXELxY0A3qP8psG%2Fimage.png?alt=media&#x26;token=2c60f72e-bf9b-43be-bcb9-7508deb7f86f" alt=""><figcaption></figcaption></figure>

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 : <br>

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FMVwIvCu1SUdy70HgNb9H%2Fimage.png?alt=media&#x26;token=91879fc5-e8dc-4518-adc0-5c917a0ff933" alt=""><figcaption><p>Simple note taking app</p></figcaption></figure>

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 .

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FUE8bDWKg3WKGGO1Hp3nS%2Fimage.png?alt=media&#x26;token=2f4146a7-76e3-4f7b-95ef-2ce487629fe1" alt=""><figcaption></figcaption></figure>

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&#x20;

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FNrVe82AMpcr6wV9vTjfa%2Fimage.png?alt=media&#x26;token=09cc86c4-cee2-47d2-b559-7cd08d6d9fa8" alt=""><figcaption></figcaption></figure>

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` <br>

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FEsa9c5kevHhgU7u17oBs%2Fimage.png?alt=media&#x26;token=9355b9b9-8fb1-4eac-b376-8de260086b77" alt=""><figcaption></figcaption></figure>

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 ) .

&#x20;

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FIM77FtzUCKctLJFzCZMd%2Fimage.png?alt=media&#x26;token=c184b5e2-335d-410f-801b-23d9bebc1ffd" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
This is second order sqli because our payload wasn't executed directly instead it was stored and executed in another portion of the application.
{% endhint %}

## Web / Outdated

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:<br>

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2F8TUEyP5E2BUP6CB4zt1k%2Fimage.png?alt=media&#x26;token=c0cba732-f03f-4690-9b3e-9b098dbe4ad5" alt=""><figcaption></figcaption></figure>

However if we checked the code we notice that our file should not contain the following:

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2Fw5OLhbi1a5eoSYCu2uZm%2Fimage.png?alt=media&#x26;token=7312b5b7-615b-487c-b38a-08ddd508da81" alt=""><figcaption></figcaption></figure>

Not only this , but also:&#x20;

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2Fz8kS0hhCX9f6PB3lHnHQ%2Fimage.png?alt=media&#x26;token=d7b11605-8ec9-492d-ab79-c5468267765f" alt=""><figcaption></figcaption></figure>

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` : <br>

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FySKJRAuXLo8BZiA56rj4%2Fimage.png?alt=media&#x26;token=49a73ee3-5d37-40ba-b81c-138f87f31f6e" alt=""><figcaption></figcaption></figure>

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: [Blog Link](https://gynvael.coldwind.pl/n/python_sandbox_escape) which says that I can import modules using `_frozen_importlib.BuiltinImporter` class , to my luck it was in the class list: <br>

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FFJrXObKko2Wvu49ObHvC%2Fimage.png?alt=media&#x26;token=ef0e0078-91f1-4912-a9f6-125b95afed06" alt=""><figcaption></figcaption></figure>

So I took it's index and used this payload : `().__class__.__bases__.[0].__subclasses__()[84].load_module("os").system("ls").read()`

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2F1fRUFrI96inx8XdbUBG5%2Fimage.png?alt=media&#x26;token=d01aa401-473d-48aa-a729-152075ce88f1" alt=""><figcaption></figcaption></figure>

Now we can easily read the flag❤

## Web / ez-sql

Again we know what to do before we start, Let's check the code directly.

The first thing caught my attention was this snippet : \
&#x20;

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2F8Lyyzjsk1JeKyTRFh6Uy%2Fimage.png?alt=media&#x26;token=d3971581-8a75-4f54-b725-7c8087a54cbb" alt=""><figcaption></figcaption></figure>

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: <br>

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FiVFZYMQCZi3CJ48BGbKD%2Fimage.png?alt=media&#x26;token=beec997f-cff7-4202-998e-ac230b083259" alt=""><figcaption></figcaption></figure>

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:&#x20;

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FZCgtDLkAEMCS7v9UUjsT%2Fimage.png?alt=media&#x26;token=24d9969e-1e0a-4228-9d79-0b102839709b" alt=""><figcaption><p>Request</p></figcaption></figure>

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FR4kDUiWc3lEoOc6OWVC0%2Fimage.png?alt=media&#x26;token=6583a6fb-3c89-437c-93e4-08dfce2e3c63" alt=""><figcaption><p>Response</p></figcaption></figure>

When adding another quote as follow : `?name=''`

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FtR13TVodMqdA5LWL8XMf%2Fimage.png?alt=media&#x26;token=5d054e37-5a31-4f26-8807-a7a9283eb558" alt=""><figcaption></figcaption></figure>

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.&#x20;

Checking the code again I saw this line : <br>

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2F9xa6QmdukwHN93Yw5Lyv%2Fimage.png?alt=media&#x26;token=b0351bff-aa3c-4788-a3e4-be1646f0926d" alt=""><figcaption></figcaption></figure>

Since I don't know what this line means I asked chatGPT: <br>

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FGmlIQ3VtvsVEeMoXprUu%2Fimage.png?alt=media&#x26;token=58700550-89cf-4c3d-ace8-aa554ac1b566" alt=""><figcaption></figcaption></figure>

hmmmm, so this means I can pass the `name` parameter as an array ?<br>

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FBUqLrFrVNCkwSXPppLUE%2Fimage.png?alt=media&#x26;token=d2ad4f5b-e4ed-4812-a2c7-abc05c13eddf" alt=""><figcaption></figcaption></figure>

This actually worked, Now we can pass name as an array instead of string then this will pass the condition assigned to it:<br>

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FwXc2VpZjgBN642P3bpjt%2Fimage.png?alt=media&#x26;token=61b022c9-07f5-4377-84c0-3c319d6a0367" alt=""><figcaption></figcaption></figure>

Now we passed the condition and we can write more than 6 characters we can enumerate for the table name: <br>

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2F3FAIxxeQiX5nylSqnb0V%2Fimage.png?alt=media&#x26;token=44948337-1838-4e2c-bafb-a3371477a34d" alt=""><figcaption></figcaption></figure>

This is the flag table, we can then select the `flag` column from it and the challenge is solved.

{% hint style="info" %}
For enumerating the database I used payloadAllTheThings repo.
{% endhint %}

## Web / back-to-the-past

We have in the code three main endpoints\
**1- Register Endpoint**

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FOq0JZ3wY8ocQCwifEdSv%2Fimage.png?alt=media&#x26;token=51a85eaa-e5ba-4a58-a39b-5bb18e6e14fb" alt=""><figcaption></figcaption></figure>

It starts first by rendering the html template then takes 2 parameters `username` & `year`&#x20;

**2- Retro Endpoint**

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FNjGxcNXEhia3gTQiabcR%2Fimage.png?alt=media&#x26;token=33a62af7-ece8-4031-858c-37006cd6b370" alt=""><figcaption></figcaption></figure>

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<br>

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FQf6nq4Cay3QjTpUuqXnP%2Fimage.png?alt=media&#x26;token=d9e50bbe-e2f9-4484-8b68-a01a26a45833" alt=""><figcaption></figcaption></figure>

**3- Login Endpoint**

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FIEPxlU6z2fpuEHwic3re%2Fimage.png?alt=media&#x26;token=0f147f95-b682-4e0a-baec-c7927861ef6a" alt=""><figcaption></figcaption></figure>

When we login , the code generates a token using `generate_token` function.

**4- JWT Tokens**

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FLpCIfXJOegl30tWGCYKx%2Fimage.png?alt=media&#x26;token=69cb705c-88ee-47bb-aba7-1cc27675abdf" alt=""><figcaption></figcaption></figure>

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.

\
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 [here](https://portswigger.net/web-security/jwt/algorithm-confusion) . I'm going to use `JWT Editor Keys` burp plugin and the steps are as follow:

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.

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2Fzhbmg0Kew6Jrv5pZdcbk%2Fimage.png?alt=media&#x26;token=6f359333-ead1-45cb-a6fe-f6b0c63c6610" alt=""><figcaption></figcaption></figure>

Now let's request `/retro` path to get the flag: <br>

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FMLayl8ATs25yzTxLKc6J%2Fimage.png?alt=media&#x26;token=3c5eab83-518a-43e4-b609-b56210b41bb2" alt=""><figcaption></figcaption></figure>

And that's it , hope you enjoyed and learned something .... good luck❤
