# CAT CTF 2023 Web Challenges

Greetings everyone, CAT CTF was organized by CAT Reloaded Team and 0xL4ugh CTF Team as member of both I had the honour to be author and organizer in this CTF.\
Also I didn't have access to any web challenge except one so I can enjoy solving them, It was totally great experience to be an organizer, author and solver in one CTF😂\
let's dive in ....

## Remotely

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2Fnc4oXyrdTrcACNVNnoKY%2Fimage.png?alt=media&#x26;token=f2861128-dc4b-4d35-bf69-42ffc31ca460" alt=""><figcaption></figcaption></figure>

We didn't have source code access to this challenge, so by heading directly to the link we find:

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FexfeWfULh8VFiS67iiDG%2Fimage.png?alt=media&#x26;token=6e453a4c-3b48-4fa6-b006-61b6ff015543" alt=""><figcaption></figcaption></figure>

As we see this we can assume that it is hinting to LFI in `mosaa` parameter or even RFI as the challenge name , when we try the basic payload : `../../../../../etc/passwd` we get :&#x20;

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FNu7ikJmQrIL0JVjGLeSK%2Fimage.png?alt=media&#x26;token=ca0e2bb8-8771-49ff-90d8-c86680509037" alt=""><figcaption></figcaption></figure>

So we are ahead of filter bypass now, trying different encoding techniques did not work so we can think in different approach than directory traversal right ? ... How about php wrappers ?\
If we supplied this payload `php://` we also get caught by the filter ... however that is not the only wrapper we have ... we have also `expect://` , `zip://` and eventually `data://` .

Using this resource : [**https://book.hacktricks.xyz/pentesting-web/file-inclusion#lfi-rfi-using-php-wrappers-and-protocols**](https://book.hacktricks.xyz/pentesting-web/file-inclusion#lfi-rfi-using-php-wrappers-and-protocols)

We find this payload :&#x20;

```
data://text/plain,<?php echo base64_encode(file_get_contents("index.php")); ?>
```

Which eventually allowed me to read `index.php` as base64 encoded data , by decoding this data we get the flag:

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FuVPc8ILtOW76IGJTqOov%2Fimage.png?alt=media&#x26;token=ff58befd-8aae-4f2c-8f50-98d7d0f4f64a" alt=""><figcaption></figcaption></figure>

## Bypassme

This challenge actually was written by me , I've seen this scenario before and I wanted to include it.

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FpJ48AJo3MDsO4B3sYDH0%2Fimage.png?alt=media&#x26;token=3ced24c0-af10-4283-83d2-42f0f312b115" alt=""><figcaption></figcaption></figure>

This time we have access to the source code, so let's take a look on it

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2F2p7JYsqpNwnKy8xN9j3k%2Fimage.png?alt=media&#x26;token=1c0e6339-9c9b-4def-b2a4-b18ca9de5525" alt=""><figcaption></figcaption></figure>

A simple flask app that takes from us `note` parameter in POST request, within this note it replaces every occurrences of `{{` , `}}`, `..` with white space.

The next thing that it searches within the note parameter for `{{ }}` and opens the file name between these curly brackets and converts it's contents to base64 encoded image. but it already omits the curly brackets the step before ... so how can we make it read files ?

If we looked at this line : `file_name = os.path.join("notes", re.sub("[{}]", "", include))` we see that it takes files from the `notes/` folder which contains 2 notes:

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2F5pnjDpvauSJTxPkkJmgE%2Fimage.png?alt=media&#x26;token=030834df-bff4-417c-b327-8d0a428a04c7" alt=""><figcaption></figcaption></figure>

So if we managed to read any note ... we can read the flag! Let's now go to the link:

If we provided our note as follow:&#x20;

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FzWCXh9SeOrCx5s2AwTHp%2Fimage.png?alt=media&#x26;token=2091894e-8f6b-4cbb-adec-ce0ce9b453d2" alt=""><figcaption></figcaption></figure>

The web app will just print the file name , but will not open it because it deletes `{{` and `}}` so in order to bypass this filter we can just type : `{..{CTF.txt}..}` as it will remove the `..` leaving the note to be : `{{CTF.txt}}`&#x20;

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FF0BanQjRNvMDm67hkKY3%2Fimage.png?alt=media&#x26;token=b285577a-a7c8-4c79-a679-fdb33ad8d2f3" alt=""><figcaption></figcaption></figure>

The base64 encoded text is the content of CTF.txt, now to read the flag we need to get out the notes folder and read the flag.txt as follow : `{..{../flag.txt}..}` but it removes the `..` .

We notice in the same line : `file_name = os.path.join("notes", re.sub("[{}]", "", include))` that it uses `os.path.join` and if absolute path is passed to it , it will take it .. In the dockerfile we see this line :  `RUN mv flag.txt /` so we know that flag is in the root directory , passing this `/flag.txt` to `path.join` it will ignore the previous path and accept the final path...leaving the final payload to be :&#x20;

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FQBpLpuxxNMPVEnRWDnTW%2Fimage.png?alt=media&#x26;token=25618385-c406-48bf-81f7-0272a30b7823" alt=""><figcaption></figcaption></figure>

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FMVP5nSRWezuvumoV5AUn%2Fimage.png?alt=media&#x26;token=beafd8e4-4101-434e-bdd9-42c0081f06b6" alt=""><figcaption></figcaption></figure>

## Read

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FIk9QLho4tUqzetWNSf6f%2Fimage.png?alt=media&#x26;token=d3537cae-566b-4f04-be2f-c137b7feab72" alt=""><figcaption></figcaption></figure>

Again we have access to source code , let's see it

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FSU2q6pPA8iUnwvTC0dEe%2Fimage.png?alt=media&#x26;token=29465325-a607-4364-8b55-300700a3bfdf" alt=""><figcaption></figcaption></figure>

Flask application, it stores the flag in the environment variables ... it has an endpoint called `readfile` that reads files through `?file` parameter.

It uses the same `os.path.join` so we can supply absolute paths as follow :&#x20;

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FgduMdcc1G7oPfWSVeMJV%2Fimage.png?alt=media&#x26;token=1ce29d05-0664-401f-a6a8-db35976af520" alt=""><figcaption></figcaption></figure>

Since we need the flag we can read the `/proc/self/environ` file right ? ... well actually no because from this line : `blocked=["proc","self"]` these 2 are blocked.

Revealing the hint we see :&#x20;

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2F6bNFBECj931JdjI2o47e%2Fimage.png?alt=media&#x26;token=c23f5d1c-5b2b-4bad-bb34-36ec3c5ef84c" alt=""><figcaption></figcaption></figure>

hmmmm , so we need another file on the system that is alternative or similar to `/proc/self/environ` ... I wanted to search through the whole system for any file that matches /proc/so I wrote this line:

```
for d in $(ls /); do echo "Trying $d" ;ls -las /$d |  grep -i "/proc/" 2>/dev/null ; done
```

The output was :&#x20;

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FkctA3cgWDSZHAha5daKF%2Fimage.png?alt=media&#x26;token=3d9bfe76-716e-4a2b-9b55-45e47eda9e8d" alt=""><figcaption></figcaption></figure>

When going to `/dev/fd` we see that it i actually a symlink to `/proc/self/fd` :&#x20;

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FbXK5TY4Gb1zf8nJAx33i%2Fimage.png?alt=media&#x26;token=ee9ee409-7d66-4930-8f63-18c25dacf6f2" alt=""><figcaption></figcaption></figure>

So if I type `/dev/fd/../environ` is like `/proc/self/fd/../environ` and we would bypass the filter also .

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FxOhhH6xyvMaGSXsD87Hg%2Fimage.png?alt=media&#x26;token=deb86b2c-7ac4-4c20-82dc-0369678accbc" alt=""><figcaption><p>And here we get the flag</p></figcaption></figure>

## Curly

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FJ38ZxtO2vL0xKW2lQUyq%2Fimage.png?alt=media&#x26;token=75b2d8ee-9af4-4f7d-9869-f6bf7a25c554" alt=""><figcaption></figcaption></figure>

This was an easy challenge that has been released later , It has source code so let's check it:

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FWJ6LxYqmiOxl76spIqxi%2Fimage.png?alt=media&#x26;token=01d512b4-e9dd-412c-bf2c-9c8d8c8aa3f2" alt=""><figcaption></figcaption></figure>

Simple curl functionality that take URL in the `url` parameter , however it checks if the URL starts with `http` or not and if it does contain `file` then it will catch us.

The trick here is that the developer forgot to use case sensitive flag in `preg_match` so if we used File:// it is actually same as file:// , To find the location of the flag we see this line in the dockerfile : `COPY flag.txt /` so the final payload would be :   `File:///flag.txt`

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2F1oIh6dZHRl2GgR0iUssS%2Fimage.png?alt=media&#x26;token=c098d2da-383f-408a-9aed-83b656d01d5a" alt=""><figcaption></figcaption></figure>

## 500

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FGlRIw3tVjfzuET2YAEXg%2Fimage.png?alt=media&#x26;token=45a8f8d0-8a13-4f45-9406-2de4989d4a97" alt=""><figcaption></figcaption></figure>

When we go to the link we find normal landing and a page called `/contact.php` which allowed us to upload files

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FaRZcMOtBvM1MoXKXdt8L%2Fimage.png?alt=media&#x26;token=4d611bfd-98be-4708-af93-b96720e2d4e7" 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%2FDBRg3SOBGNeLulhsmgxB%2Fimage.png?alt=media&#x26;token=a5a7d0b7-a87f-4d74-920d-3cdef8c12b10" alt=""><figcaption><p>Response</p></figcaption></figure>

That's it, any file we upload we just get this alert even if bypassed the filter we don't know the uploads directory ... this was a dead end until a hint has been released:

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2F59PPIru9SbYqBZuv8WSt%2Fimage.png?alt=media&#x26;token=35f56f08-1dd8-4302-a726-4e272ccf64c6" alt=""><figcaption></figcaption></figure>

Hmmm , so this is part of the challenge ... now we can change our approach to force the application to through errors. This can be done by passing invalid data types , invalid data formats etc ...

When I changed the name from `file` to `file[]` as a form of invalid data type I saw this error:

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FCGbnfA8cmWtNxnHbkR73%2Fimage.png?alt=media&#x26;token=0f9e8aa1-d578-4804-8bb3-ccdf32dd19fd" alt=""><figcaption></figcaption></figure>

It didn't reveal the uploads path , but at least we know what we are dealing with now `getimagesize()` function in PHP.

After searching **ALOT** about this function, I found that it has exceptions which makes it throw errors , these exceptions were:&#x20;

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FJcSIeWRVD4pDDypWD3VK%2Fimage.png?alt=media&#x26;token=0493da50-b58c-45e5-9ce3-66341a7e719f" alt=""><figcaption></figcaption></figure>

The third point says that if the filename is impossible to be accessed then it will throw warning ... How to do this ? First I thought of passing invalid URLs with invalid images but none of the worked until I though of passing very long filename as follow:

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FY55bixpUwuOm5uUypAB8%2Fimage.png?alt=media&#x26;token=7c855584-9a7e-42a7-9e79-fbe4b7e04553" alt=""><figcaption></figcaption></figure>

And the response was:

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2F3TAy54M4bC248BW1dNqw%2Fimage.png?alt=media&#x26;token=e48c9510-a566-4c86-9c86-8bf3e2156acb" alt=""><figcaption></figcaption></figure>

Nice , we managed to get the directory name which is : `Sup3r_S3cret_H1dd3n` we can even confirm the existence of it when we visit it.

Now after we get the directory name we can access our PHP files and get RCE right ? actually no because of the naming convention we can't get the correct name of the file , as you notice at the beginning there is some hex characters. But after revisiting the challenge description it was easier than I thought : `once u find it u will find ur gift at Flag.txt` ......!\
We can access the flag directly under the hidden directory:&#x20;

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FwFBBmwIw3P1JMxx2WUbo%2Fimage.png?alt=media&#x26;token=c68408ad-3485-4158-87df-e9417743171c" alt=""><figcaption></figcaption></figure>

## Legacy Developer 1 & 2

This challenge consisted of 2 parts , I included them in one section as I solved both with the same solution. The main idea in the first one is to use the private key the developer forgot to sign the JWT while in the second one they removed the keys. But I didn't use the keys in both so let's start.

We have code access this time so let's check , and again ...  The code is the same for the 2 challenges except for the keys.

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2Ft5eyE3XMUWIHplx6sZVj%2Fimage.png?alt=media&#x26;token=150dfa6d-1535-4c24-a4ed-2f63d32ce3d6" alt=""><figcaption></figcaption></figure>

First it stores the flag and the private key in environment variables, it then creates the DB and insert the user admin in it with the id=1, it defines variable of keypath with the value of `/app/secrets/publickey` .

#### Register Route

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FgOhHsjClVMnr4Cn644vA%2Fimage.png?alt=media&#x26;token=3ba761eb-9b74-44cf-855f-df78407bcdb0" alt=""><figcaption></figcaption></figure>

It simply takes from us json parameters which are `name` and `password` , of course we can't register as an admin.

#### Login Route

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FLGrjZ4BD9ZpjKp9WUt5P%2Fimage.png?alt=media&#x26;token=b7d9bc00-2106-46e2-9c5b-861a31b1c4a2" alt=""><figcaption></figcaption></figure>

The login route accepts the same parameters and then checks the database , if it is right it will return us the JWT token , the most important keys in the token are `iss` and `id` , If not it will return 401

#### Public Key Route

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FIq8FOd03xZcmKTi4vEP5%2Fimage.png?alt=media&#x26;token=d48a9bc1-6c2e-43a7-b404-dbd0f95bc1b8" alt=""><figcaption></figcaption></figure>

It simply opens the public key file when a request to this route is done

#### Flag Route

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2F9VBif8c4YbOgd6E5tPfQ%2Fimage.png?alt=media&#x26;token=ef0642a3-7986-4c49-9b6e-2a35c9fa86b6" alt=""><figcaption></figcaption></figure>

When accessing this route, The function `token_required` is called .. If we are admin then it will display the flag else it will return access denied . Sol let's check what does token\_required function does:

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2F2Q1VpVCnyU4jWtiF2tU2%2Fimage.png?alt=media&#x26;token=0b8ed8f6-a9a6-47da-a838-a02906df1aa4" alt=""><figcaption></figcaption></figure>

First it checks whether the token is present in a header called : `x-access-tokens` or not

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FZIZoXCqERbL6AUGsauSn%2Fimage.png?alt=media&#x26;token=f2db30be-ba82-44f9-adbe-2b875e04c029" alt=""><figcaption></figcaption></figure>

Second thing it gets the header of JWT and then searches specifically for `iss` if it does exist then it will check it's value to start with `/api/secrets/publickey` and if it is .. it will make a request to the public\_key\_url and use the response as the public key. So simply the steps are:

* Get the headers of JWT
* search for `iss` and confirm that it starts with `/api/public/secretkey`&#x20;
* Make a request to the url with the iss appended to it.

Now let's register and login to get our valid token

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FkLmqeLv8CT9mrE3B4MBh%2Fimage.png?alt=media&#x26;token=d1ce0f40-df02-41e5-9316-b15d1c6df8e2" alt=""><figcaption></figcaption></figure>

When accessing the flag route with this token we get:

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FA5ER5gpriSoFoZfFGZZn%2Fimage.png?alt=media&#x26;token=a224d56c-4e95-40c4-b724-38e401fdfc71" alt=""><figcaption></figcaption></figure>

The app gets the headers of JWT using this line :&#x20;

```
unverified_header = jwt.get_unverified_header(token)
```

So let's try it on our token to see what it gets:

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FmS6Qn6Hd2MbKsJP58GOF%2Fimage.png?alt=media&#x26;token=53d40d58-39ae-4eed-9ec9-d95b0146a97d" alt=""><figcaption></figcaption></figure>

So we don't actually have the iss inside it ... We can test with another token from jwt.io, as the app only searches in the headers without any signing

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2Fa1rsPfnxY9MO7mXaduzX%2Fimage.png?alt=media&#x26;token=6ff6a85d-9763-4e2d-943d-be686fa69114" alt=""><figcaption><p>dummy token with iss specified</p></figcaption></figure>

When using this token we get:

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FcL5hPzAH8uqdxXVmio1K%2Fimage.png?alt=media&#x26;token=59a1630a-0dab-4bc4-b2ea-f16ac9c72cf7" alt=""><figcaption></figcaption></figure>

It says invalid issuer , that means that it accepts the token with our issuer but it does not start with the /api/...&#x20;

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FAsOGbiDd6qEIIEFRTPA9%2Fimage.png?alt=media&#x26;token=2bb70547-b4e9-40b8-bb9f-828956b1e567" alt=""><figcaption><p>When supplying /api/.. as the value</p></figcaption></figure>

Now we know that it uses the following URL to sign the token : `http://localhost:5000/api/secrets/publickey` , can we control this URL to request our server and use our own public key ?  ... Upon visiting the code again I noticed this endpoint

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FH9A1C3o19NzKbcp44WN2%2Fimage.png?alt=media&#x26;token=ed8e2ca3-7e1b-4a1a-b5e2-15ccc841d7db" alt=""><figcaption></figcaption></figure>

Logout , it also takes the `r` parameter which will redirect us to any URL we need:

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FFWas6DbISSzEEC0L5XC7%2Fimage.png?alt=media&#x26;token=ae5d2fd1-f913-4b01-bd7b-99b192705fa3" alt=""><figcaption><p>redirection to google</p></figcaption></figure>

That is actually awesome , we can redirect to our server by providing the following url : `http://localhost:5000/logout?r=OUR-SERVER` in the iss ... but wait , it should start with /api/secrets/publickey right ?\
Here I thought of using directory traversal :&#x20;

{% code overflow="wrap" %}

```
URL/logout?r == URL/api/secrets/publickey../../../../logout?r
```

{% endcode %}

And this will work, to test this we can use ngrok and local server&#x20;

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FIrc4qNpgPTcpjTHlwzfm%2Fimage.png?alt=media&#x26;token=92873669-0706-4d40-83f1-efdd4175bc77" alt=""><figcaption><p>The token</p></figcaption></figure>

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2F9mRWFGD6UvI2HSFfTDP0%2Fimage.png?alt=media&#x26;token=24f2c68e-1835-44cd-afe8-58076cfca4d1" alt=""><figcaption><p>Connection received</p></figcaption></figure>

We succeed to make it points to our local server , which would be easy now to sign the token with our own public key:

* I got the public key from jwt.io (You can generate your own)
* I saved it on my sever naming it `public_key.pem` (same format as the code says)
* Add the following payload in the iss : `/api/secrets/publickey../../../../logout?r=URL/public_key.pem`
* Change the id to be equal 1 (to be admin)
* Finally access the flag route with the token

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2F9JKsYIOjeqTqGetoOEAV%2Fimage.png?alt=media&#x26;token=c733986e-9a8d-45ef-b4ce-b42b2f9e8ebf" alt=""><figcaption><p>How the token looks</p></figcaption></figure>

And finally ......

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2Fa02JOkH0jM1i7vH4nEDn%2Fimage.png?alt=media&#x26;token=66ff945e-7e10-43d6-9772-60b710365489" alt=""><figcaption><p>We got the flag</p></figcaption></figure>

<figure><img src="https://49018334-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQa5lZLJzKij2hNo1ER5S%2Fuploads%2FufFOA4mn27MeCKPowUUx%2Fimage.png?alt=media&#x26;token=c61bfdcd-3ead-4b49-ab76-9513cc5606fd" alt=""><figcaption><p>The flag from the another challenge using the same approach</p></figcaption></figure>

That's it .... I hope you've learned something in this CTF ... If you need any further explanation do not hesitate to ask me ... and thank you
