Ah! I solved this challenge just yesterday so it’s nice to see this, it was a fun new technique to learn, and I can talk a little bit about it as requested by @pry0cc, even if… well… that wasn’t directed to me, but still, I want to contribute.
This is a deserialization flaw, what this means is that we can provide a stream of arbitrary data to abuse the logic of the application receiving our stream, as this stream isn’t handled safely and it can be dangerous enough to cause remote code execution or denial of service attacks.
In this specific case, the flaw can be found in the serialize.js module for node.js. In the application above, the one used in a Hack The Box CTF challenge, our web browser sends the node.js server a cookie encoded in Base64 containing JSON data, something like this:
{"username":"Dummy","number":"2","location":"random_place"}
The cookie is taken as input by a vulnerable web application and sent to the unserialize() function in order to convert it into an object that can be used in the code, the lines that do this operation are as follows:
var str = new Buffer(req.cookies.profile, 'base64').toString(); // obtains and decodes the cookie
var obj = serialize.unserialize(str); // converts our cookie from a buffer to an object, the vulnerability takes place here
You might be wondering why this flaw happens exactly, and we can take a look at the source code for serialize.js to get our answer, let’s analyze these few lines of code:
if(obj[key].indexOf(FUNCFLAG) === 0) {
obj[key] = eval('(' + obj[key].substring(FUNCFLAG.length) + ')');
}
Keep in mind that obj is an array containing all the different fields of our cookie (in the case of our targeted web application of course), and key is used as index of said array inside of a for() loop to refer to the single fields. The snippet of code is taken from line 75 of the source code, I will link the GitHub repository at the end of the post. Basically, what this code does is checking whether the item number key of our cookie begins with FUNCFLAG, and if it does, it removes FUNCFLAG from our item and passes the remaining text to the eval() function, and basically, this function executes code. So, if we wanted to make it short, FUNCFLAG is nothing but a constant used to mark text that has to be passed to eval() and thus interpreted as code.
So what happens if we manage to include FUNCFLAG in our cookie followed by an instruction we wish to execute? Simple, the code will be executed! On line 2 of the source code we find this:
var FUNCFLAG = '_$$ND_FUNC$$_';
So, if we wish to make our web application execute some code we have to craft our cookie so that it includes that string above to tell the unserialize() function we have something special for eval(). Here’s a very simple example to close the instance of node.js at our own will:
{"username":"Dummy","number":"2","location":"random_place","custom_code":"_$$ND_FUNC$$_process.exit(0)"}
All we have to do is encode the cookie back to Base64 and forward it to the web application, which will give it right to the unserialize() function, passing process.exit(0) to eval(). And there’s that, that’s how this exploit works. In the video OP shows how to obtain a reverse shell thanks to nodejsshell.py, the concept is the same, but instead of a lame process.exit(0) our cookie will include an encoded shell that will connect back to us.
I hope this was helpful, if anyone has any questions or corrections feel free to go ahead!
Source for serialize.js ----> https://github.com/luin/serialize/blob/master/lib/serialize.js