{"id":"CVE-2026-43999","revision":null,"records":[{"revision":"3485cba7290c4110bd2c0652cda23140","observedAt":"2026-10-23T15:48:27.903+00:00","statement":{"source":"cve-program","id":"CVE-2026-43999","aliases":[],"title":"vm2: NodeVM builtin allowlist bypass via `module` builtin's `Module._load` allows sandbox escape","description":"vm2 is an open source vm/sandbox for Node.js. Prior to 3.11.0, NodeVM's builtin allowlist can be bypassed when the module builtin is allowed (including via the '*' wildcard). The module builtin exposes Node's Module._load(), which loads any module by name directly in the host context, completely bypassing vm2's builtin restriction. This allows sandboxed code to load excluded builtins like child_process and achieve remote code execution. This vulnerability is fixed in 3.11.0.","state":0,"publishedAt":"2026-05-13T17:21:22.308+00:00","modifiedAt":"2026-09-07T12:05:02.063+00:00","affected":[{"ecosystem":"Vendor product","name":"vm2","ranges":[],"versions":[],"rawVersionEvidence":"{\n                    \"vendor\": \"patriksimek\",\n                    \"product\": \"vm2\",\n                    \"versions\": [\n                        {\n                            \"version\": \"< 3.11.0\",\n                            \"status\": \"affected\"\n                        }\n                    ]\n                }"}],"metrics":[{"system":"CVSS 3.1","vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H","score":9.9,"severity":"CRITICAL"}],"references":["https://github.com/patriksimek/vm2/security/advisories/GHSA-947f-4v7f-x2v8"],"sourceUrl":"https://www.cve.org/CVERecord?id=CVE-2026-43999","license":"CVE Program Terms of Use","key":"cve-program:CVE-2026-43999","identifiers":["CVE-2026-43999"]},"retained":false,"historicalAffected":[]},{"revision":"35772263bd5445fe842cac4c87160e57","observedAt":"2026-11-07T09:37:44.541+00:00","statement":{"source":"euvd","id":"EUVD-2026-30069","aliases":["CVE-2026-43999","GHSA-947f-4v7f-x2v8"],"title":"vm2 is an open source vm/sandbox for Node.js. Prior to 3.11.0, NodeVM's builtin allowlist can be bypassed when the module builtin is allowed (including via the '*' wildcard). The module builtin exposes Node's Module._load(), which loads any…","description":"vm2 is an open source vm/sandbox for Node.js. Prior to 3.11.0, NodeVM's builtin allowlist can be bypassed when the module builtin is allowed (including via the '*' wildcard). The module builtin exposes Node's Module._load(), which loads any module by name directly in the host context, completely bypassing vm2's builtin restriction. This allows sandboxed code to load excluded builtins like child_process and achieve remote code execution. This vulnerability is fixed in 3.11.0.","state":0,"publishedAt":null,"modifiedAt":null,"affected":[],"metrics":[{"system":"EUVD CVSS 3.1","vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H","score":9.9,"severity":null}],"references":["https://github.com/patriksimek/vm2/security/advisories/GHSA-947f-4v7f-x2v8"],"sourceUrl":"https://euvd.enisa.europa.eu/vulnerability/EUVD-2026-30069","license":"ENISA EUVD · source terms and attribution apply","enrichment":{"sourceStatus":null,"weaknesses":[],"productCriteria":[],"sourceDates":{"datePublished":"May 13, 2026, 5:21:22 PM","dateUpdated":"Sep 7, 2026, 12:05:02 PM"},"relatedAdvisories":[{"id":"RHSA-2026:50850","title":"Red Hat Security Advisory: Red Hat Ansible Automation Platform 2.1 security update","vulnerabilityIds":["CVE-2026-9277","CVE-2026-43997","CVE-2026-43998","CVE-2026-43999","CVE-2026-44000","CVE-2026-44001","CVE-2026-44002","CVE-2026-44003","CVE-2026-44004","CVE-2026-44005","CVE-2026-44006","CVE-2026-44007","CVE-2026-44008","CVE-2026-44009","CVE-2026-45411","CVE-2026-47131","CVE-2026-47135","CVE-2026-47137","CVE-2026-47139","CVE-2026-47140","CVE-2026-47141","CVE-2026-47209","CVE-2026-48068","CVE-2026-48801","CVE-2026-59877"]}],"note":"EUVD source date text has no stated timezone and is retained without inventing a UTC instant. Related bulletin membership is not an alias, common cause or applicability claim. A missing vector with a zero score is treated as unrated. Complete vendor/product and underlying source evidence remains in the original JSON."},"key":"euvd:EUVD-2026-30069","identifiers":["EUVD-2026-30069","CVE-2026-43999","GHSA-947f-4v7f-x2v8"]},"retained":false,"historicalAffected":[]},{"revision":"371591f787674dd2a654cfe863adf999","observedAt":"2026-10-27T11:12:09.678+00:00","statement":{"source":"nvd","id":"CVE-2026-43999","aliases":[],"title":"vm2 is an open source vm/sandbox for Node.js. Prior to 3.11.0, NodeVM's builtin allowlist can be bypassed when the module builtin is allowed (including via the '*' wildcard). The module builtin exposes Node's Module._load(), which loads any…","description":"vm2 is an open source vm/sandbox for Node.js. Prior to 3.11.0, NodeVM's builtin allowlist can be bypassed when the module builtin is allowed (including via the '*' wildcard). The module builtin exposes Node's Module._load(), which loads any module by name directly in the host context, completely bypassing vm2's builtin restriction. This allows sandboxed code to load excluded builtins like child_process and achieve remote code execution. This vulnerability is fixed in 3.11.0.","state":0,"publishedAt":"2026-05-13T18:16:16.45+00:00","modifiedAt":"2026-10-26T17:10:03.27+00:00","affected":[],"metrics":[{"system":"CVSS 3.1 · security-advisories@github.com · Secondary","vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H","score":9.9,"severity":"CRITICAL"},{"system":"CVSS 3.1 · 0b0ca135-0b70-47e7-9f44-1890c2a1c46c · Secondary","vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H","score":9.9,"severity":"CRITICAL"}],"references":["https://github.com/patriksimek/vm2/security/advisories/GHSA-947f-4v7f-x2v8","https://access.redhat.com/errata/RHSA-2026:50850","https://access.redhat.com/security/cve/CVE-2026-43999","https://bugzilla.redhat.com/show_bug.cgi?id=2477196","https://security.access.redhat.com/data/csaf/v2/vex/2026/cve-2026-43999.json"],"sourceUrl":"https://nvd.nist.gov/vuln/detail/CVE-2026-43999","license":"NIST NVD usage terms; underlying source attribution retained","enrichment":{"sourceStatus":"Modified","weaknesses":["CWE-863","CWE-829"],"productCriteria":["cpe:2.3:a:vm2_project:vm2:*:*:*:*:*:node.js:*:*"],"sourceDates":{"published":"2026-05-13T18:16:16.450","lastModified":"2026-10-26T17:10:03.270"},"relatedAdvisories":[],"note":"NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON."},"key":"nvd:CVE-2026-43999","identifiers":["CVE-2026-43999"]},"retained":false,"historicalAffected":[]},{"revision":"b8f91f8aee144c65b50a6bbee960d6b5","observedAt":"2026-10-24T13:17:17.793+00:00","statement":{"source":"osv","id":"GHSA-947f-4v7f-x2v8","aliases":["CVE-2026-43999"],"title":"vm2 has a NodeVM builtin allowlist bypass via `module` builtin's `Module._load` that allows sandbox escape","description":"## Summary\nNodeVM's `builtin` allowlist can be bypassed when the `module` builtin is allowed (including via the `'*'` wildcard). The `module` builtin exposes Node's `Module._load()`, which loads any module by name directly in the host context, completely bypassing vm2's builtin restriction. This allows sandboxed code to load excluded builtins like `child_process` and achieve remote code execution.\n\n## Severity\n**Critical** (CVSS 3.1: 9.9)\n\n`CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H`\n\n- **Attack Vector:** Network — sandboxed code is typically received from external sources (user-submitted scripts, plugin code)\n- **Attack Complexity:** Low — no special conditions required; `['*', '-child_process']` is a common, documented pattern\n- **Privileges Required:** Low — attacker needs only the ability to submit code to the sandbox, which is the intended use case\n- **User Interaction:** None\n- **Scope:** Changed — escape from sandbox boundary to host system\n- **Confidentiality Impact:** High — arbitrary command execution on the host\n- **Integrity Impact:** High — arbitrary command execution on the host\n- **Availability Impact:** High — arbitrary command execution on the host\n\n## Affected Component\n- `lib/builtin.js` — `makeBuiltinsFromLegacyOptions()` (lines 109-117) — includes `module` in `'*'` expansion\n- `lib/builtin.js` — `addDefaultBuiltin()` (lines 86-90) — loads `module` with generic readonly wrapper\n- `lib/builtin.js` — `SPECIAL_MODULES` (line 61) — does NOT include `module`\n\n## CWE\n- **CWE-863**: Incorrect Authorization\n\n## Description\n\n### Root Cause: The `module` builtin provides unrestricted host module loading\n\nWhen `builtin: ['*', '-child_process']` is configured, `makeBuiltinsFromLegacyOptions` iterates over `BUILTIN_MODULES` and adds all modules not explicitly excluded:\n\n```js\n// lib/builtin.js:40\nconst BUILTIN_MODULES = (nmod.builtinModules || Object.getOwnPropertyNames(process.binding('natives')))\n    .filter(s=>!s.startsWith('internal/'));\n\n// lib/builtin.js:109-117\nif (Array.isArray(builtins)) {\n    const def = builtins.indexOf('*') >= 0;\n    if (def) {\n        for (let i = 0; i < BUILTIN_MODULES.length; i++) {\n            const name = BUILTIN_MODULES[i];\n            if (builtins.indexOf(`-${name}`) === -1) {\n                addDefaultBuiltin(res, name, hostRequire);\n            }\n        }\n    }\n```\n\nNode's `builtinModules` includes `'module'` (verified: `require('module').builtinModules.includes('module')` → `true`). Since only `'-child_process'` is excluded, `'module'` passes the filter and gets added.\n\nThe `module` builtin is NOT in `SPECIAL_MODULES` (which only covers `events`, `buffer`, `util`), so it gets the generic loader:\n\n```js\n// lib/builtin.js:86-90\nfunction addDefaultBuiltin(builtins, key, hostRequire) {\n    if (builtins.has(key)) return;\n    const special = SPECIAL_MODULES[key];\n    builtins.set(key, special ? special : vm => vm.readonly(hostRequire(key)));\n}\n```\n\nThis wraps Node's `Module` class in a readonly proxy and hands it to the sandbox.\n\n### The readonly proxy does not prevent method calls\n\n`ReadOnlyHandler` (bridge.js:940-983) only overrides mutation traps: `set`, `setPrototypeOf`, `defineProperty`, `deleteProperty`, `isExtensible`, `preventExtensions`. It does NOT override `get` or `apply`, which are inherited from `BaseHandler`.\n\n`BaseHandler.apply()` (bridge.js:665-677) forwards function calls directly to the host context:\n\n```js\napply(target, context, args) {\n    const object = getHandlerObject(this);\n    let ret;\n    try {\n        context = otherFromThis(context);\n        args = otherFromThisArguments(args);\n        ret = otherReflectApply(object, context, args);\n    } catch (e) {\n        throw thisFromOtherForThrow(e);\n    }\n    return thisFromOther(ret);\n}\n```\n\nSo `Module._load('child_process')` is forwarded to Node's native `Module._load` in the host context, which loads `child_process` without any vm2 allowlist check.\n\n### Inconsistent defense: some builtins are isolated, `module` is not\n\nThe codebase IS aware that certain builtins need special handling:\n\n- `events`: Gets a complete sandbox-native reimplementation via `lib/events.js`\n- `buffer`: Custom loader that only exposes the `Buffer` class\n- `util`: Custom loader that replaces `inherits` with a sandbox-safe version\n\nBut `module` — which provides access to the host's entire module loading infrastructure via `Module._load`, `Module._resolveFilename`, etc. — gets no special treatment at all.\n\n### Full execution chain\n\n1. Host configures `NodeVM` with `builtin: ['*', '-child_process']`\n2. `makeBuiltinsFromLegacyOptions` adds `'module'` to allowed builtins (not excluded)\n3. Sandbox code calls `require('module')` → resolver finds `'module'` in builtins → `loadBuiltinModule('module')`\n4. Loader calls `vm.readonly(hostRequire('module'))` → returns readonly proxy of Node's `Module` class\n5. Sandbox reads `Module._load` → `BaseHandler.get()` returns proxied function\n6. Sandbox calls `Module._load('child_process')` → `BaseHandler.apply()` forwards to host\n7. Host's `Module._load` loads `child_process` natively (no vm2 check involved)\n8. `child_process` module proxied back to sandbox\n9. Sandbox calls `child_process.execSync('id')` → executes on host → RCE\n\n## Proof of Concept\n\n```js\nconst { NodeVM } = require('vm2');\n\n// Developer thinks child_process is blocked\nconst vm = new NodeVM({\n  require: {\n    builtin: ['*', '-child_process'],\n    external: false,\n  },\n});\n\nconst out = vm.run(`\n  const Module = require('module');\n  // Module._load bypasses vm2's builtin allowlist entirely\n  const cp = Module._load('child_process');\n  module.exports = cp.execSync('id').toString();\n`, 'poc.js');\n\nconsole.log(out.trim()); // prints host uid/gid — RCE achieved\n```\n\n## Impact\n- **Complete builtin allowlist bypass**: Any configuration that allows the `module` builtin (including `['*', '-X']` patterns) can load ANY builtin, including explicitly excluded ones.\n- **Remote code execution**: Sandboxed code can execute arbitrary commands on the host via `child_process.execSync`.\n- **Common configuration affected**: The `['*', '-child_process', '-fs']` pattern is documented and widely used by developers who want \"all builtins except dangerous ones.\"\n- **No special conditions**: Unlike environment-dependent attacks, this works on every Node.js version, every OS, and every vm2 deployment that uses the `'*'` wildcard.\n- **Additional attack surfaces via `module`**: Beyond `_load`, the `Module` class also exposes `_resolveFilename`, `_cache`, `_pathCache`, and other internals that could be abused.\n\n## Recommended Remediation\n\n### Option 1: Exclude `module` from `BUILTIN_MODULES` entirely (Preferred)\n\nThe `module` builtin provides unrestricted host module loading and should never be exposed to the sandbox:\n\n```js\n// lib/builtin.js:40\nconst DANGEROUS_BUILTINS = new Set(['module', 'worker_threads', 'cluster']);\n\nconst BUILTIN_MODULES = (nmod.builtinModules || Object.getOwnPropertyNames(process.binding('natives')))\n    .filter(s => !s.startsWith('internal/') && !DANGEROUS_BUILTINS.has(s));\n```\n\nThis prevents `module` from being included even with the `'*'` wildcard. Consider also blocking `worker_threads` and `cluster` which can spawn processes.\n\n### Option 2: Add `module` to `SPECIAL_MODULES` with a safe wrapper\n\nIf `module` must be accessible, provide a sandbox-safe version that only exposes safe APIs:\n\n```js\n// lib/builtin.js\nconst SPECIAL_MODULES = {\n    events: { /* ... existing ... */ },\n    buffer: defaultBuiltinLoaderBuffer,\n    util: defaultBuiltinLoaderUtil,\n    module: function defaultBuiltinLoaderModule(vm) {\n        // Only expose safe, read-only metadata — no _load, no _resolveFilename\n        return vm.readonly({\n            builtinModules: [...nmod.builtinModules],\n            // Omit _load, _resolveFilename, _cache, createRequire, etc.\n        });\n    }\n};\n```\n\n**Tradeoff**: Breaks sandbox code that legitimately uses `Module` APIs, but those APIs are inherently unsafe in a sandbox context.\n\n## Credit\nThis vulnerability was discovered and reported by [bugbunny.ai](https://bugbunny.ai).","state":0,"publishedAt":"2026-05-07T04:08:55+00:00","modifiedAt":"2026-05-14T20:49:43.499907+00:00","affected":[{"ecosystem":"npm","name":"vm2","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"3.10.5"},{"kind":"fixed","version":"3.11.0"}]}],"versions":["3.10.5"],"rawVersionEvidence":null}],"metrics":[{"system":"CVSS_V3","vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H","score":null,"severity":null},{"system":"Publisher rating","vector":null,"score":null,"severity":"CRITICAL"}],"references":["https://github.com/patriksimek/vm2/security/advisories/GHSA-947f-4v7f-x2v8","https://nvd.nist.gov/vuln/detail/CVE-2026-43999","https://github.com/patriksimek/vm2"],"sourceUrl":"https://github.com/advisories/GHSA-947f-4v7f-x2v8","license":"GitHub Advisory Database · CC-BY-4.0","key":"osv:GHSA-947f-4v7f-x2v8","identifiers":["GHSA-947f-4v7f-x2v8","CVE-2026-43999"]},"retained":false,"historicalAffected":[]}],"coverage":[{"source":"cisa-kev:global","scope":"Complete CISA KEV catalog; all vendors. Missing entries do not establish absence of exploitation.","generation":"2026-11-07T17:07:37.1829709+00:00","complete":true,"records":1769,"lastAttemptAt":"2026-11-07T17:07:37.1829709+00:00","lastSuccessAt":"2026-11-07T17:07:37.3549159+00:00","error":null,"lastVerifiedAt":"2026-11-07T17:07:37.3549172+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"cve-program","scope":"Official CVE Program baseline and cumulative hourly delta; all vendors, published and rejected records","generation":"cve-bb5c9d5a08063ef076042e33dd72a7d0ffa4c4487efdcf3106e29ba55ec2ad57-715e663378e343ca50c7397586f2d227eca3ece57a7a73d5f48eaadc71eb9f69","complete":true,"records":420933,"lastAttemptAt":"2026-11-07T17:07:36.095653+00:00","lastSuccessAt":"2026-11-07T17:07:36.0305426+00:00","error":null,"lastVerifiedAt":"2026-11-07T17:07:36.095653+00:00","snapshotPublishedAt":"2026-11-07T16:30:46+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"euvd","scope":"Rolling EUVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global EUVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-ce98ec71edc04bf7b5b5ed5b4106954c","complete":true,"records":13633,"lastAttemptAt":"2026-11-07T17:19:56.3581416+00:00","lastSuccessAt":"2026-11-07T17:19:56.354+00:00","error":null,"lastVerifiedAt":"2026-11-07T17:19:56.354+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13685,"expectedIdentifiers":13685,"failedIdentifiers":0},{"source":"first-epss:nuget","scope":"FIRST EPSS for explicit CVE aliases in the collected public NuGet archive. Not a global EPSS backfill.","generation":"2026-11-07T17:07:37.1829709+00:00","complete":true,"records":1096,"lastAttemptAt":"2026-11-07T17:07:37.1829709+00:00","lastSuccessAt":"2026-11-07T17:07:37.3564231+00:00","error":null,"lastVerifiedAt":"2026-11-07T17:07:37.3564236+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"nvd","scope":"Rolling NVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global NVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-57173bad19094db6a6a9d0334caa764d","complete":true,"records":13645,"lastAttemptAt":"2026-11-07T17:08:02.7189692+00:00","lastSuccessAt":"2026-11-07T17:08:02.715+00:00","error":null,"lastVerifiedAt":"2026-11-07T17:08:02.715+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13685,"expectedIdentifiers":13685,"failedIdentifiers":0},{"source":"osv:Go","scope":"Complete OSV Go ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"go-0eb4d6dde62dffacd040ad992d4fd5a65cd7d0dc5f5f4cfa8b41967fc6f9ca5f","complete":true,"records":9914,"lastAttemptAt":"2026-11-07T17:07:36.7559298+00:00","lastSuccessAt":"2026-11-06T20:10:36.6450506+00:00","error":null,"lastVerifiedAt":"2026-11-07T17:07:36.7559298+00:00","snapshotPublishedAt":"2026-11-06T20:02:57+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:NuGet","scope":"Complete OSV NuGet ecosystem archive, including GitHub Advisory Database records; NuGet version matching only","generation":"nuget-fde804134ecdda0ddd5ff5c26ee1fff5a6fa659311f7ef2e51c6ad54964db0e2","complete":true,"records":1971,"lastAttemptAt":"2026-11-07T17:07:36.1583672+00:00","lastSuccessAt":"2026-11-06T19:03:17.0399903+00:00","error":null,"lastVerifiedAt":"2026-11-07T17:07:36.1583672+00:00","snapshotPublishedAt":"2026-11-06T18:17:57+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:crates.io","scope":"Complete OSV crates.io ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"crates.io-335633e78a15c9cde10a6025895b5617717087fb945489ae7e15d377bacf34a9","complete":true,"records":3055,"lastAttemptAt":"2026-11-07T17:07:37.135444+00:00","lastSuccessAt":"2026-11-06T16:03:00.7615789+00:00","error":null,"lastVerifiedAt":"2026-11-07T17:07:37.135444+00:00","snapshotPublishedAt":"2026-11-06T16:02:48+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:npm","scope":"Complete OSV npm ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"npm-40ad6183a951565ace1520a9b4d631e1d0bfb1d4bf7c5574c375032559f3ff53","complete":true,"records":231292,"lastAttemptAt":"2026-11-07T17:07:36.637447+00:00","lastSuccessAt":"2026-11-07T10:29:00.4821259+00:00","error":null,"lastVerifiedAt":"2026-11-07T17:07:36.637447+00:00","snapshotPublishedAt":"2026-11-07T09:32:47+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null}]}