Intigriti's monthly challenge 1125: From none to fun, a JWT attack for accessing admin panel to gain SSTI RCE in profile modification
Intigriti’s November 2025 challenge
The challenge started simply by checking all UI elements and its interaction.
I noticed the shop categories, the order process and checkout.
The site offers a different number of interactions. There could be a SQL injection, maybe it’s php stuff.
It’s all red herring, as it turns out later.
Those who are not focused and do not work systematically will be punished.
Like me. If you don’t pay attention, you’ll have to pay.
I started to register first and noticed the JWT token as session cookie. My first thought was to immediately check the JWT none algorithm. In that case the server does not validate the JWT payload against the signature and we are free to put our payload into the JWT.
My registration got back this session cookie.
Set-Cookie: token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjo2LCJ1c2VybmFtZSI6Im1lYWQiLCJyb2xlIjoidXNlciIsImV4cCI6MTc2MzU4MTg3Mn0.SYAV8Vis3XhFvmt_lgQiFDrqBJuZpTxz-qlb9HEb49g;
A JWT is tranmitted as base64.base64.base64 and consists of three parts
-
the JWT header
{"alg":"HS256","typ":"JWT"} - the JWT payload
{ "user_id": "6", "username": "mead", "role": "user", "exp": 1763581872 } - the JWT signature, which the server can use to verify authenticity.
Maybe I need to be admin?
{
"user_id": "6",
"username": "admin",
"role": "user",
"exp": 1763581872
}
The dashboard shows my changed name, but nothing really changed
Ahh, the user role smells like the solution. Lets change it to admin!
{
"user_id": "6",
"username": "admin",
"role": "admin",
"exp": 1763581872
}
In dashdoard I see an Admin Panel now. Strike!
At this time, I am not focused anymore and waste hours of my time.
My vision tells me that I may need to adjust the configuration of the products.
However, the buttons are disabled.
I start to extend the payload with possible additional account permissions. I’ve been poking around, a lot.
Finally, I build a JWT token with maximum extension.
I wanted to activate these buttons on the server side.
Was it a wasted time? Certainly not.
It gave me a deeper insight into the possibilities of privileges, scopes, permissions and maybe hidden flags represented in a JWT.
{
"user_id": "1",
"username": "admin",
"role": "admin",
"superuser": true,
"level": 99,
"privileges": [
"read",
"write",
"modify",
"delete"
],
"scope": "edit:all",
"canEdit": true,
"isEditor": true,
"editAccess": true,
"edit": true,
"permissions": [
"read",
"write",
"modify",
"delete"
],
"exp": 1763585032
}
I wanted to make a stop after this JWT and saw the My Profile button in the Admin Panel.
Ohh, is this new and I got something new?
NO, I was just BLIND all the time. My huge JWT was not the reason, just my lost focus.
Accessing the My Profile page allows me to edit account information.
Back on track my first thought was the right one.
Although I had initially thought about PHP stuff, I suddenly found myself leaning more towards Python and the jinja2 template engine.
Verify it in input field Display Name by entering 7 got the result 49 after save.
Is it really Python? Entering `` got back Python stuff.
All right, now it’s quite simple.
I don’t want to open a reverse shell just to make my work easier. So I only use selected commands and make it easy for myself. I search for the pattern of the flag.
Input in Display Name
will result in Current Display Name output
/app/.aquacommerce/019a82cf.txt:INTIGRITI{019a82cf-ca32-716f-8291-2d0ef30bea32}
/app/templates/public/index.html: Intigriti's November challenge by INTIGRITI
/app/templates/public/index.html: The flag in the format INTIGRITI{.*}
Got it. The flag file is called /app/.aquacommerce/019a82cf.txtand contains the flag INTIGRITI{019a82cf-ca32-716f-8291-2d0ef30bea32}
Post-analysis of the server /app files proved that only the amin role was necessary in the JWT, no fancy stuff and no functions behind the edit / delete buttons in manage products.
Well, next time I’ll be quicker.
However, one unpleasant experience during the challenge was that the website was not multi-user thread safe.
Throughout the day, many other attempts could be seen in the profile administration, which spoiled the necessary approach.
Disclaimer
The information provided is released “as is” without warranty of any kind. The publisher disclaims all warranties, either express or implied, including all warranties of merchantability. No responsibility is taken for the correctness of this information. In no event shall the publisher be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages, even if the publisher has been advised of the possibility of such damages.
The contents of this advisory are copyright (c) 2025 by psytester and may be distributed freely provided that no fee is charged for this distribution and proper credit is given.