2linenodejs
Description
Web | 13 solves
Sorry for my bad coding style :(
Author: ginoah
Solution
Prototype Pollution
Taking a look at the source, we see quite clearly that there is a prototype pollution here.
JSON.parse
will allow the __proto__
key, storing it as ['__proto__']
instead (which surprisingly works as a key when used here):
Great! We have a prototype pollution - how do we leverage it to an RCE?
require() Gadget
After performing the pollution, we don't have much of a choice where we want to go. Either nothing happens and process.exit()
is called, or we cause an exception and require('./usage')
is called. Causing an exception is pretty simple and I actually stumbled upon it early on when testing simple payloads.
If one of the key-value pairs is a mapping to null
, then Object.entries(V)
will yield a TypeError
since null
cannot be converted to an Object
.
If readPackageScope
returns false
, then the destructuring assignment should leave pkg
and pkgPath
as undefined
, since the right-hand side is {}
. But if we pollute __proto__.data
and __proto__.path
, then we can control pkg
and pkgPath
.
But what is pkg
and pkgPath
? We could look at readPackageScope
and find out that it calls
With this knowledge, we can confirm that the following exploit allows us to load any JavaScript file.
preinstall.js Gadget
Immediately we see in this script that we have child_process.execFileSync
being called, which looks promising.
First off, to reach this code path we could need to pollute npm_config_global
to a truthy value.
One issue is that because the regex matches up to the first space character, our JSON cannot have any spaces.
To get around this, we use ${IFS}
. For instance, we could pollute npm_execpath
to --eval=require('child_process').execSync('sleep${IFS}5')
.
The final payload was using wget
and command substitution to exfiltrate the /readflag
output.
This gives us the flag on our listening HTTP server.
Last updated
Was this helpful?