Attack ChainAIDerived
Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain
-
AccessSubmit crafted JS payload to vm2 sandbox endpoint
-
DeliveryInvoke Buffer.concat with attacker-controlled totalLength
-
ExploitBypass bufferAllocLimit via unwrapped host allocator path
-
ExecutionSynchronously commit host external memory before timeout fires
-
PersistRepeat calls or scale totalLength to exhaust container memory limit
-
ImpactHost process terminated by OOM killer
Vulnerability Assessment
| Weakness |
CWE
OWASP Top 10:2025
A06:2025 - Insecure Design
ASVS 5.0
V2 - Validation and Business Logic Resource allocation limits
MITRE ATT&CK
1 technique across 1 tactic: Impact The technique list with ATT&CK links and the kill-chain view is part of the Basic plan and up. |
| Exploitation Conditions | Exploitation requires three concrete conditions: (1) the target application runs vm2 version 3.11.0-3.11.5 with the |
| Impact | The CVSS 4.0 score of 8.7 (AV:N/AC:L/AT:N/PR:N/UI:N/VA:H) accurately reflects high availability impact with no complexity or privilege barrier. The most operationally significant risk signal is the combination of a publicly available, confirmed PoC and the synchronous, timeout-immune nature of the allocation: a single sandbox call commits memory before any reactive control can act. For applications deployed with container memory limits - the environments the advisory specifically names (Docker, Kubernetes, AWS Lambda) - a single crafted request terminates the host process. The vulnerability is strictly DoS; no confidentiality or integrity impact applies, and no sandbox escape is present. Risk is meaningfully bounded by whether the application exposes vm2 sandbox execution to untrusted callers - internal or authenticated-only deployments face lower real-world exposure. EPSS data is not provided in the input; KEV listing is absent, indicating no confirmed nation-state or ransomware-actor exploitation at time of analysis, though this may change given the PoC availability and the high-value DoS class in containerized infrastructure. |
| Exploit ScenarioDerived | An attacker submits a 200-byte JavaScript payload to an application endpoint that evaluates user-supplied code in a vm2 sandbox: |
Get an e-mail when this CVE is exploited, patched or re-scored. Free account.
Alert meAnalysisAIv2 – Aug 17, 2026
Denial-of-service in vm2 (Node.js sandbox library) versions 3.11.0-3.11.5 allows sandbox code to bypass the bufferAllocLimit DoS mitigation via two unwrapped allocation paths - Buffer.concat(list, totalLength) and Buffer.from({length: N}) - committing unbounded host external memory in a single synchronous C++ call that V8's timeout mechanism cannot interrupt. A publicly available PoC confirms that a 200-byte sandbox payload inflates host RSS by hundreds of megabytes per invocation, making a single crafted request sufficient to OOM-kill a host process running inside a Docker container, Kubernetes pod, or AWS Lambda function. No public exploit identified in CISA KEV at time of analysis, though functional exploit code is embedded in the GHSA advisory itself.
Earlier versions of this analysis (1)
Denial-of-service via memory exhaustion in vm2 (npm) affects all versions through 3.11.5: untrusted sandbox code can bypass the embedder-configured bufferAllocLimit cap by calling Buffer.concat(list, totalLength) or Buffer.from({length: N}), causing the host Node.js process to commit arbitrarily large external memory allocations in a single synchronous C++ call that V8's timeout mechanism cannot interrupt. The bufferAllocLimit option - introduced specifically to prevent this DoS class in GHSA-6785-pvv7-mvg7 - wraps Buffer.alloc and Buffer.allocUnsafe but leaves these two allocator paths uncapped, meaning a 200-byte sandbox payload can drive hundreds of megabytes of RSS increase per call. A functional public proof-of-concept is included in the advisory; no active exploitation (CISA KEV) has been reported.
RemediationAI
Upgrade to vm2 3.11.6 immediately; this release installs sandbox-side wrappers via connect(...) for Buffer.concat, Buffer.from, and Buffer.copyBytesFrom that consult localBufferAllocLimit before delegating to the host allocator, restoring the intended invariant. The fix commit is available at https://github.com/patriksimek/vm2/commit/3ffb315512e634cf85c447375e85d6c83d00a4bd and the full release is at https://github.com/patriksimek/vm2/releases/tag/3.11.6. If an immediate upgrade is blocked, the only viable compensating control is to enforce a hard container or process memory ceiling - for example, Docker --memory or Kubernetes resources.limits.memory - and ensure the process restarts automatically on OOM via a supervisor or orchestrator restart policy; accept that the host process will terminate under active attack, which is a degraded-but-recoverable posture. Restricting sandbox-execution endpoints to authenticated or allowlisted callers reduces exposure surface but does not neutralize the vulnerability for any caller who retains sandbox access. Disabling bufferAllocLimit entirely is not a mitigation - doing so removes the only cap on the already-known parent vulnerability class.
Affected VersionsENISA
Affected Packages
Source: OSV (Open Source Vulnerabilities)
Affects your stack
Affects 1 package in npm
Checking this CVE against your own stacks and servers is part of the Basic plan and up.
Technical ContextAI · show details
vm2 is a widely-used Node.js sandboxing library (pkg:npm/vm2) designed to execute untrusted JavaScript in isolation. The bufferAllocLimit option, introduced in 3.11.0 as a fix for GHSA-6785-pvv7-mvg7, instruments sandbox-visible Buffer constructors in lib/setup-sandbox.js with a checkBufferAllocLimit(size) guard at lines 424, 433, 474, 480, 488, 496, 504, and 510. The root cause is CWE-770 (Allocation of Resources Without Limits or Throttling): two allocation paths are never wrapped. Buffer.concat(list, totalLength) internally calls Node's Buffer.allocUnsafe(totalLength) directly on the host side, bypassing the sandbox proxy entirely. Buffer.from({length: N}) triggers Node's fromArrayLike path, which allocates a buffer of size N before filling it - no actual array of length N needs to exist, making the {length: N} object a pure synthetic trigger. Both paths reach the host V8/libuv C++ allocator with an attacker-controlled size, and neither consults localBufferAllocLimit. The allocation is synchronous and completes before V8's interrupt-based timeout can fire, meaning the memory is committed to the host's RSS regardless of any timeout setting.
Know when Docker gets its next vulnerability
- Watch products and get an email when something new is published
- Check this CVE against your own stack and servers
- Exploit and PoC references, threat intelligence from 22 sources
No credit card · 7-day full trial
Severity by source
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N
Network vector with no privilege requirement, as applications exposing vm2 sandboxes typically accept untrusted code without authentication; impact is availability-only with unchanged scope since no sandbox escape occurs.
Primary rating from Vendor (https://github.com/patriksimek/vm2).
CVSS VectorVendor: https://github.com/patriksimek/vm2
Lifecycle Timeline
3DescriptionCVE.org
Summary
vm2 bufferAllocLimit cap bypassed by Buffer.concat and Buffer.from arrayLike
The bufferAllocLimit option introduced in 3.11.0 (GHSA-6785-pvv7-mvg7) caps host-side Buffer allocations driven by sandbox code, the way embedders opt into timeout. The cap wraps Buffer.alloc, Buffer.allocUnsafe, Buffer.allocUnsafeSlow, and the deprecated Buffer(N) / new Buffer(N) forms. Two other API paths reach the same host C++ allocator with an attacker-controlled size and are not capped: Buffer.concat(list, totalLength) and Buffer.from(arrayLike) with a fake length. Sandbox code can use either to allocate an arbitrary number of host external bytes in a single call, defeating the explicit DoS mitigation the embedder configured.
Details
lib/setup-sandbox.js installs checkBufferAllocLimit at every wrapped entry to host Buffer allocation:
alloc()atlib/setup-sandbox.js:474and theconnect(alloc, host.Buffer.alloc)at line 480.allocUnsafe()at line 488 andconnect(allocUnsafe, host.Buffer.allocUnsafe)at line 496.allocUnsafeSlow()at line 504 andconnect(allocUnsafeSlow, host.Buffer.allocUnsafeSlow)at line 510.BufferHandler.applyat line 424 andBufferHandler.constructat line 433 for the deprecatedBuffer(N)/new Buffer(N)numeric-first-arg paths.
Buffer.concat is not wrapped. The sandbox-visible Buffer.concat is therefore the bridge proxy of the host Buffer.concat, which calls into Node's Buffer.allocUnsafe(totalLength) internally without going through the sandbox-side allocUnsafe wrapper. Same for Buffer.from when the argument is array-like ({length: N}): Node's fromArrayLike allocates a buffer of size N before the iteration that fills it. Neither of those allocator paths consult localBufferAllocLimit.
The mitigation rationale documented in docs/ATTACKS.md Category 23 explicitly enumerates the surfaces that were considered and either capped (Buffer.alloc family) or punted to follow-up (new Uint8Array(N), new ArrayBuffer(N), String.prototype.repeat). Buffer.concat(list, totalLength) is not listed in either group, and Buffer.from(arrayLike) is mentioned only as "bounded by source array size which had to be allocated through some other path first" -- which is not true for the {length: N} form, because no array of length N actually exists.
A single call from sandbox to Buffer.concat([Buffer.from('a')], 50 * 1024 * 1024) allocates 50 MiB of host external memory. The allocation itself is a single synchronous host C++ call that timeout cannot interrupt, exactly like the original advisory. The zero-fill that follows is interruptible, but the memory is already committed by the time the interrupt could fire, so the embedder's container memory budget is the only ceiling. The same pattern in a loop, or with a larger totalLength, drives RSS up by hundreds of megabytes per call.
The fix uses the existing checkBufferAllocLimit(size) helper and a sandbox-side wrapper installed via connect(...) -- one for Buffer.concat that sums the totalLength (or falls back to summing list lengths) and one for Buffer.from that recognises the array-like-with-numeric-length branch.
PoC
'use strict';
const { VM, NodeVM } = require('vm2');
function ext() { return Math.round(process.memoryUsage().external / 1024 / 1024); }
function tryBypass(label, code) {
const ext0 = ext();
let buf;
try { buf = code(); }
catch (e) {
console.log(`[${label}] CAPPED -- ${String(e).split('\n')[0]}`);
return;
}
console.log(`[${label}] BYPASSED -- got ${buf && buf.length} bytes (external +${ext() - ext0} MB)`);
}
console.log('Cap is configured at 1024 bytes.\n');
const vm1 = new VM({ bufferAllocLimit: 1024 });
tryBypass('VM Buffer.alloc(50MB) ',
() => vm1.run('Buffer.alloc(50 * 1024 * 1024)'));
const vm2 = new VM({ bufferAllocLimit: 1024 });
tryBypass('VM Buffer.concat 50MB ',
() => vm2.run('Buffer.concat([Buffer.from("a")], 50 * 1024 * 1024)'));
const vm3 = new NodeVM({ bufferAllocLimit: 1024 });
tryBypass('NodeVM Buffer.concat 50MB ',
() => vm3.run('module.exports = Buffer.concat([Buffer.from("a")], 50 * 1024 * 1024);'));
const vm4 = new VM({ bufferAllocLimit: 1024 });
tryBypass('VM Buffer.from({length: 8MB})',
() => vm4.run('Buffer.from({length: 8 * 1024 * 1024})'));Run with node poc.js against vm2@3.11.3:
Cap is configured at 1024 bytes.
[VM Buffer.alloc(50MB) ] CAPPED -- RangeError: Buffer allocation size 52428800 exceeds bufferAllocLimit 1024
[VM Buffer.concat 50MB ] BYPASSED -- got 52428800 bytes (external +50 MB)
[NodeVM Buffer.concat 50MB ] BYPASSED -- got 52428800 bytes (external +50 MB)
[VM Buffer.from({length: 8MB})] BYPASSED -- got 8388608 bytes (external +8 MB)Process RSS climbs by the same amount each call, confirming a real host C++ allocation rather than a sandbox-realm-only effect.
Impact
This is the same DoS class GHSA-6785-pvv7-mvg7 was filed for: untrusted sandbox code amplifying a small payload into a large synchronous host external-memory allocation that V8's timeout cannot preempt. In the environments the advisory cites -- Docker memory limits, Kubernetes pods, AWS Lambda -- a single 200-byte sandbox payload can drive a multi-hundred-megabyte RSS jump and OOM the host process.
The Category 23 fix was specifically scoped to "cap host Buffer external allocation" and embedders are documented to opt into bufferAllocLimit as their layered defense against this class. The two paths above are uncapped, so an embedder that has configured bufferAllocLimit: 32 * 1024 * 1024 (the value recommended in the README's Hardening recommendations) is still vulnerable to the exact attack the option was designed to prevent. The mitigation invariant -- "every Buffer external allocation driven by sandbox code is capped by bufferAllocLimit" -- does not hold.
No sandbox escape; pure DoS.
Blast Radius
ecosystem impact- 22 npm packages depend on vm2 (6 direct, 16 indirect)
Ecosystem-wide dependent count for version 3.11.6.
Articles & Coverage 1
Executive ViewAI
vm2 versions 3.11.0-3.11.5 contain a critical denial-of-service vulnerability with publicly available exploit code that allows attackers to crash production services with a single 200-byte request, causing complete service unavailability through uncontrolled memory exhaustion. Organizations using vm2 to sandbox untrusted code-including SaaS platforms, code execution services, and serverless deployments-face immediate risk of infrastructure disruption and potential cascading failures across dependent systems.
No vendor-released patch identified at time of analysis. Interim mitigations: (1) Apply strict per-invocation memory limits via Node.js heap limits or OS-level process controls; (2) Implement rate limiting and request throttling on any endpoint invoking vm2; (3) Ensure container/pod memory limits are configured and enforced; (4) If feasible, disable or restrict use of Buffer.concat() and Buffer.from({length}) patterns within sandboxes; (5) Monitor vendor advisories daily for patch availability and plan immediate upgrade upon release.
Threat Intelligence Overview
How this decision was reached
The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.
What every action level means
SSVC AssessmentENISA
Priority Score
Attack Technique
Denial of Service attacks render applications or systems unavailable by overwhelming resources or triggering failure conditions.
An issue was discovered in Appsmith before 1.52. Rated critical severity (CVSS 9.8), this vulnerability is remotely expl
runc through version 1.0-rc6 (used in Docker before 18.09.2) contains a container escape vulnerability that allows attac
Netmaker makes networks with WireGuard. Rated high severity (CVSS 7.5), this vulnerability is remotely exploitable, no a
Unauthenticated remote code execution in Marimo ≤0.20.4 allows attackers to execute arbitrary system commands via the `/
The News & Blog Designer Pack - WordPress Blog Plugin - (Blog Post Grid, Blog Post Slider, Blog Post Carousel, Blog Post
Path traversal in JFrog Artifactory (CWE-22) enables an authenticated low-privilege user to write data outside the inten
Docker 1.3.2 allows remote attackers to execute arbitrary code with root privileges via a crafted (1) image or (2) build
Remote code execution in Gogs through 0.14.2 allows authenticated users (and unauthenticated attackers on default-config
Unauthenticated remote command injection in SGWBOX N3 firmware 2.0.25 lets attackers execute arbitrary operating-system
Remote code execution in Flowise before 3.1.2 allows any authenticated user (or API caller with chatflow view/update per
Remote code execution in NocoBase Workflow Script Node (npm @nocobase/plugin-workflow-javascript) allows authenticated l
Docker Desktop Community Edition before 2.1.0.1 allows local users to gain privileges by placing a Trojan horse docker-c
EUVD-2026-60461
GHSA-gmc2-2x9w-cgh9
NVD
CVE.org
EPSS
Vulnrichment
VulDB
Exploit-DB