GHSA-p69m-4f92-2v84: PraisonAI: Remote Code Execution via Sandbox Escape in `codeMode` Tool
SummaryThe `codeMode` tool in `src/praisonai-ts/src/tools/builtins/code-mode.ts` uses `new Function()` with a `with(sandbox)` pattern to execute LLM-generated code. The blocklist-based "sandbox" can be trivially bypassed via `Function('return this')()` to recover the global object, followed by `global.require()` with string concatenation to evade the blocklist regex. This allows full arbitrary code execution on the host system. This affects all deployments where the code-mode tool is enabled for agents.Details**Vulnerable code (lines 187–191):**The `code` parameter comes from LLM tool call arguments (the `execute` method at line 104). Before execution, a regex-based blocklist is applied (lines 108–136):**Three fundamental weaknesses:****`with(sandbox)` does not provide isolation.** The `with` statement in JavaScript adds an object to the scope chain but does NOT prevent accessing the global object. The sandbox object sets `process: undefined` and `require: undefined`, but these are recovered via the global scope:**Blocklist evasion via string concatenation.** The regex `/require\s*\(\s*['"]child_process['"]\s*\)/` requires the literal string `'child_process'` or `"child_process"` inside `require()`. Using `require('child_' + 'process')` bypasses this because the regex sees a variable concatenation, not a literal string.**`Function('return this')()` is not blocked.** None of the blocklist patterns match `Function(`, `return this`, or `global.require`.PoC**Setup:** Clean checkout at commit `d5f1114a`, Node.js v20.20.0 (tested environment).**Positive trigger — full RCE with sandbox escape (OBSERVED OUTPUT):****Observed output (executed in this environment):**The escape was confirmed by executing the exact code-mode sandbox pattern (`new Function('sandbox', 'with (sandbox) { ... }')`) with the blocklist applied first. ALL blocklist patterns were bypassed, and the `id` command returned the real system user ID.**Negative control — blocklist correctly catches direct require:****Negative control — blocklist correctly catches eval:****Cleanup:** No persistence needed; the code runs in-process.ImpactAn attacker who can influence the `code` parameter of the `codeMode` tool (via crafted prompts to an AI agent using praisonai-ts) achieves **full arbitrary code execution** on the host system. This includes:**Read/write any file** accessible to the process user**Execute arbitrary system commands** via `child_process`**Exfiltrate environment variables** containing API keys, tokens, and credentials**Install persistent backdoors** by writing to startup files**Move laterally** in containerized environmentsSuggested remediationThe `with(sandbox)` + blocklist pattern is fundamentally insecure and cannot be fixed with regex improvements. Replace it with:**Use `vm` module with proper context isolation:****Or use `isolated-vm`** for true process-level isolation with separate V8 isolates.**Or run code in a subprocess** (like the Python `_execute_code_sandboxed` pattern already used in `python_tools.py`) with a clean environment and resource limits.If a blocklist approach must be retained, add patterns for:`Function(` / `new Function``constructor` / `__proto__` / `prototype``return this` / `return global``global` / `globalThis` / `window`
But note: blocklist approaches are inherently fragile and will continue to have bypasses.
CVSS v3.1: 9.8