By inspecting the source files, let’s first note that the flag filename is not simply
flag but it is generated randomly, starting from
flag and then appending 5 random characters, as we can see in
This is a simple express web server, serving one page with a route to
/api/submit triggered by the
submit button. What instantly struck me was the following line of code (in
For whatever reason (to be pawned, I guess 😆) it was using the
unflatten method from the
challenge/static/js/main.js , we see, indeed, that the input from the web form is handled as follows:
So it first
stringify an object:
'song.name': 'my fav song'
and then it unflattens it to be something like:
name: 'my fav song'
With some digging, one can easily discover that
flat versions < 5.0.2 (and in our case, it is
5.0.0 ) is vulnerable to Prototype Pollution.
As a proof of concept, one can, in fact, run the following code:
And without having defined the
user variable, it will output the string
The last important thing to notice, is that the server is using
pug , and in particular the
The details of this exploit can be read at https://blog.p6.is/AST-Injection/ , and it is really awesome. Our implementation would look like (in Postman):
But what’s going on here?
First we send the
song.name with one of the songs, so that we trigger the
pug.compile . Then, we pollute the
block prototype which is used by
pug and we inject our command:
process.mainModule.require('child_process').execSync('cat /app/flag* > /app/static/out')
This command will be executed by Node.js, which in turn will execute
cat /app/flag* > /app/static/out , namely, it will print the content of the
flag* (we need to add the
* since we do not know the last 5 characters) into a file named
/app/static , so that we can navigate to
<URL>/static/out and see the flag!