Skip to content

Security2 publishersIndependently confirmed3 min readPublished Updated

isolated-vm's ExternalCopy type confusion turns the sandbox into a path to the host

A time-of-check/time-of-use bug in the library many platforms use to run untrusted JavaScript lets guest code reach the host process. Fixes are in 6.2.0 and 7.0.1, and there is still no CVE.

The Watch · Security desk

Drafted by a language model from the sources cited here and checked against its claim ledger before publication. How we use AISend a correction

Photograph accompanying isolated-vm's ExternalCopy type confusion turns the sandbox into a path to the host
Photo: securityweek.com

What happened

  • A critical-severity type confusion in the isolated-vm Node.js library could allow threat actors to achieve remote code execution on the host system; the bug impacts ExternalCopy and was detailed by EndorLabs.
  • Through isolated-vm, developers can access the V8 JavaScript engine's Isolate interface to build completely isolated JavaScript environments.
  • Each Isolate is a completely separated V8 instance with its own heap memory, execution state and garbage collector, enabling multiple sandboxed JavaScript instances on one machine without a container or virtual machine.
  • isolated-vm is widely used for executing untrusted JavaScript code within a V8 Isolate.
  • ExternalCopy is the function used to copy data across Isolates; it serializes the data in one Isolate and reconstructs it in the other instance.

Compiled by The WatchSomething wrong?How this is made

Why it matters

If your product runs customer-supplied JavaScript inside isolated-vm, the isolation you were charging for has been nominal. EndorLabs has disclosed a critical-severity type confusion in the library's ExternalCopy function that can end in remote code execution on the host system [1].

isolated-vm gives Node.js developers access to V8's Isolate interface, so each sandboxed script gets its own heap, execution state and garbage collector without a container or a VM around it [2][3]. That cheapness is the point, and it is why the library is widely used to execute untrusted JavaScript [4]. It is also why the failure matters: for many embedders, the C++ binding is the entire boundary.

The bug sits in the data path between Isolates. ExternalCopy serializes a value in one Isolate and reconstructs it in the other [5]. As a performance optimisation it accepts a transferList: large ArrayBuffers are named, and the backing memory is moved by detaching the buffer from the source and handing it to the destination [6]. During reconstruction the code walked the byte array list twice, and the second pass trusted what the first pass had seen [7]. Because an element of the transfer_list JavaScript array can be defined as a getter, the two walks need not return the same value, and an attacker can use that time-of-check/time-of-use gap to get an attacker-controlled pointer dereferenced [8].

The ExternalCopy constructor itself is only reachable from the host, which sounds like a containment argument until you read the next step: a guest can go through ivm.Reference, the mechanism the host uses to expose anything at all into the sandbox, to assemble the malicious transferList and fire the bug [9]. Outcomes run from a crash of the host process to control-flow hijack, which is where the RCE claim comes from [10].

The advisory is blunt about the blast radius. "Any embedder that runs untrusted code in an isolate and shares even one Reference into it is affected. Host code that passes a caller-influenced array as transferList is affected directly, without any guest," it reads [14]. So there are two populations here: sandbox operators who thought a single shared Reference was a harmless convenience, and host code with no guest anywhere near it that forwards a caller-influenced array into a transfer [16].

Patches are in isolated-vm 6.2.0 and 7.0.1, and the fix is to stop user JavaScript running during the copy at all rather than to sanity-check the re-read [11]. That is the right shape of fix, because as EndorLabs puts it, the vulnerability "lived in the native glue code," a layer written in a memory-unsafe language that manipulates raw V8 handles and backing-store pointers and re-reads attacker-controlled JavaScript objects mid-operation, where "a single unchecked cast on a re-read value was enough to turn a correct isolation primitive into a full escape" [12].

The awkward part for defenders: the bug has not been assigned a CVE identifier [13]. Dependency tooling keyed to CVE feeds has nothing to match on, so the check reduces to reading the isolated-vm version in your lockfile against 6.2.0 and 7.0.1 [15]. Also audit your own host code for any transferList built from caller input, since that path does not need a hostile guest to be a problem [16].

Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories