The vulnerability in @nyariv/sandboxjs is a classic sandbox escape caused by improper handling of return values from function calls and property access within the sandboxed environment. The core issue was that the sandbox did not wrap or sanitize values returned from function calls or when accessing object properties. This allowed an attacker to execute code that, while appearing safe, could leak references to powerful objects from the host's (Node.js) environment.
The primary exploit vector demonstrated in the Proof of Concept involves calling Object.values(this) or Object.entries(this). Inside the sandbox, this refers to the global object. The Object.values function, when called on the sandboxed global object, would return an array containing all its properties, including a raw, unsandboxed reference to the host's Function constructor. By accessing this Function constructor from the returned array, an attacker could create and execute arbitrary code with the full permissions of the underlying Node.js process, completely bypassing the sandbox.
The patch addresses this by modifying the core execution logic in src/executor.ts. Specifically, the handlers for LispType.Call (function calls) and LispType.Prop (property access) were updated:
- For
LispType.Call: The return value of every function call is now intercepted. If the value is a function, it is wrapped in a sandboxed equivalent. If it is the global globalThis object, it is replaced by the sandbox's internal global object.
- For
LispType.Prop: When a property is accessed on an object, the value is checked. Similar to function calls, if the property is a function or globalThis, it is wrapped or replaced accordingly.
These changes ensure that malicious code cannot obtain direct references to powerful, exploitable objects from the host environment, effectively closing the sandbox escape vulnerability.