VDB
Sign up
CRITICAL10.0

GHSA-wxhw-j4hc-fmq6

SandboxJS has Sandbox Escape via Unprotected AsyncFunction Constructor

Quick fix

GHSA-wxhw-j4hc-fmq6 — @nyariv/sandboxjs: upgrade to the fixed version with the command below.

npm install @nyariv/sandboxjs@0.8.26

Details

### Summary A sandbox escape vulnerability due to `AsyncFunction` not being isolated in `SandboxFunction`

### Details

The library attempts to sandbox code execution by replacing the global `Function` constructor with a safe, sandboxed version (`SandboxFunction`). This is handled in `utils.ts` by mapping `Function` to `sandboxFunction` within a map used for lookups.

However, the library did not include mappings for `AsyncFunction`, `GeneratorFunction`, and `AsyncGeneratorFunction`. These constructors are not global properties but can be accessed via the `.constructor` property of an instance (e.g., `(async () => {}).constructor`).

In `executor.ts`, property access is handled. When code running inside the sandbox accesses `.constructor` on an async function (which the sandbox allows creating), the `executor` retrieves the property value. Since `AsyncFunction` was not in the safe-replacement map, the `executor` returns the actual native host `AsyncFunction` constructor.

Constructors for functions in JavaScript (like `Function`, `AsyncFunction`) create functions that execute in the global scope. By obtaining the host `AsyncFunction` constructor, an attacker can create a new async function that executes entirely outside the sandbox context, bypassing all restrictions and gaining full access to the host environment (Remote Code Execution).

### PoC

```js const sandbox = require('@nyariv/sandboxjs'); const s = new sandbox.default();

const payload = ` const af = async () => {}; // .constructor returns the host AsyncFunction constructor because it's not intercepted const AsyncConstructor = af.constructor; console.log("AsyncConstructor name:", AsyncConstructor.name); // Create a function that executes outside the sandbox const func = AsyncConstructor("return process.mainModule.require('child_process').execSync('id').toString()"); // Execute RCE const p = func(); p.then(proc => { console.log(proc); }); `;

try { s.compile(payload)().run(); } catch (e) { console.error("Bypass failed:", e.message); } ```

Run above script in nodejs. If you run it in browser, change the `AsyncConstructor` argument by returning `window` object.

### Impact

A Remote Code Execution, attacker may be able to run an arbitrary code.

Are you affected?

Enter the version of the package you're using.

Affected packages

npm/@nyariv/sandboxjs
Introduced in: 0Fixed in: 0.8.26
Fixnpm install @nyariv/sandboxjs@0.8.26

References