Notice: Please do not spawn too many instances since our server resource is limited.
You can check the source code and run it in your local machine before do that.
Each instances are alive only for 5 minutes.
But don't worry! You can spawn again even if your instance expired.
We can send cookie=__proto__ so setting = settings["__proto__"] which is Object.prototype. Then, we can set key via ctx.params.name and value via ctx.request.body.value to achieve prototype pollution.
So, the question is, what can we do then?
The core function for this challenge is this line: child_process.spawnSync('/usr/games/cowsay', [ctx.request.query.say], { timeout: 500 });, so I guess it's the key.
I checked the nodejs docs, there is one line got my attention:
If the shell option is enabled, do not pass unsanitized user input to this function. Any input containing shell metacharacters may be used to trigger arbitrary command execution.
After polluted Object.prototype.shell, we can do command injection!
There is one more thing, we need to let shell=true not shell="true" so application/x-www-form-urlencoded doesn't work, we need to send application/json instead.
Description
Enjoy your cowsay life with our Cowsay as a Service! You can spawn your private instance from https://cowsay-as-a-service.chal.acsc.asia/.
Notice: Please do not spawn too many instances since our server resource is limited. You can check the source code and run it in your local machine before do that. Each instances are alive only for 5 minutes. But don't worry! You can spawn again even if your instance expired.
Source code:
Writeup
It's obviously that there is a prototype pollution vulnerability:
We can send
cookie=__proto__
sosetting = settings["__proto__"]
which isObject.prototype
. Then, we can set key viactx.params.name
and value viactx.request.body.value
to achieve prototype pollution.So, the question is, what can we do then?
The core function for this challenge is this line:
child_process.spawnSync('/usr/games/cowsay', [ctx.request.query.say], { timeout: 500 });
, so I guess it's the key.I checked the nodejs docs, there is one line got my attention:
Let's try it:
After polluted
Object.prototype.shell
, we can do command injection!There is one more thing, we need to let
shell=true
notshell="true"
soapplication/x-www-form-urlencoded
doesn't work, we need to sendapplication/json
instead.Exploit:
output:
Actually, we don't even need two commands, we can just send:
"say": "$FLAG"
becauseshell=true
makes$
a metacharacters instead of a literal.