Hack The Box - Didactic Octo Paddles

March 26, 2023

Didactic Octo Paddles

Didactic Octo Paddles is a web challenge that features an application with a vulnerable implementation of JWT token validation using the None algorithm. This can be leveraged to forge a JWT that impersonates the admin user which then allows an SSTI vulnerability to be exploited in the /admin route, leading to LFI and discovery of the flag.

Initial visit to the app showed a login page:

login page

/routes/index.js contained a /register route:

register route

I registered a new user, mike:

register mike

After logging in, the page was a basic e-commerce site that allows users to add items to their cart and view their cart, but not much beyond that in terms of functionality.

e-commerce page

/routes/index.js also had an admin route:

admin route

So, I took the cookie from my current session as the user mike and used Burp Suite to try and access /admin which responded with a 403 error and the message: You are not an admin

mike JWT

burp not admin

I went to JWT.IO to view the details of my token:

mike token JWT.io

JWTs are usually encoded using Base64 and can consist of either two or three parts, depending on the algorithm used. The token typically consists of a header, a payload, and a signature. The header specifies the algorithm type (e.g., HS256) and the token type (JWT). The payload contains the user ID, the issued at timestamp, and expiration time. Any algorithm besides the None algorithm has a third part which is the signature. The signature is used to verify the authenticity and integrity of the JWT.

Further analysis of the code showed an insecure implementation of validating the alg parameter in the header. Since the code in AdminMiddleware.js validates the alg parameter by attempting to filter out strings that contain "none", it can be bypassed by simply passing in "None" as the value for the "alg" parameter because it's case sensitive.

AdminMiddleware.js

database.js revealed that the only other user in the database was admin, so it could be assumed that the id of this user was 1:

database.js

To create a JWT that will use the None algorithm and impersonate the admin user by setting the id parameter to 1, I needed to create two parts (JWTs using the None algorithm don't require signatures):

  • The first part is the header which will specify the None algorithm and token type of JWT

  • The second part is the payload and will set the id to 1, and use the same iat and exp of my previous JWT since those are valid values on the server.

I ran the following commands to generate both parts of the new token:

create JWT

The generated token details (note that it uses the None algorithm and has an id of 1):

new token JWT.io

Then, I tried accessing the admin route with the forged JWT and received a 200 response which brought up the admin dashboard page:

burp admin route 200

The response body showed the two users in the database: admin and mike

active users

The code shown below in the /admin route uses the jsrender template engine, and it's implemented in a way that directly injects usernames into the HTML without properly sanitizing the input. This makes it vulnerable to server-side template injection which could be leveraged to get local file inclusion.

index.js admin route jsrender

admin.jsrender injects the username data into the HTML which is then displayed on the /admin dashboard page:

admin.jsrender template

So, I sent the following payload from HackTricks which registers a new user by injecting JavaScript code within the username field that executes a command to retrieve the contents of the /etc/passwd file. This can then be viewed by visiting the /admin page.

{"username":"{{:\"pwnd\".toString.constructor.call({},\"return global.process.mainModule.constructor._load('child_process').execSync('cat /etc/passwd').toString()\")()}}","password":"test"}

burp send payload /etc/passwd

Next, I visited the /admin route to reveal the contents of /etc/passwd:

burp response /etc/passwd

The flag was located in /flag.txt:

Dockerfile

So, I sent the same payload as before to the /register route, but changed cat /etc/passwd to cat /flag.txt:

burp payload cat /flag.txt

Finally, I visited the /admin route to get the flag:

flag


CTF Writeups | InfoSec Topics

Written by Mike Garrity

Email RSS