← Back to Feed CACHED · 2026-10-13 15:15:15 · CACHE_KEY CVE-2026-61445
CVE-2026-61445 · CWE-22 · Disclosed 2026-07-11

PraisonAI AICoder Arbitrary File Write and Command Execution via LLM Tool Calls

ASSESSED — NOISGATE
Vendor
—
—
—
Reassessed
—
—
—
Verdict: —
Do you agree?
01 · The Real Story

Leaving the keys in the ignition of a car only three people on the block own

CVE-2026-61445 affects the AICoder UI component in PraisonAI, an open-source Python-based AI agent framework. Versions before 4.6.78 expose two LLM tool-call functions—write_to_file and execute_command—with zero path validation and zero command sanitization. The write_to_file handler uses bare os.path.join which does not reject absolute paths (os.path.join('/app', '/etc/passwd') returns /etc/passwd), enabling writes to any filesystem location. The execute_command handler passes unsanitized input straight to a shell. Because the standard Docker deployment runs as root, exploitation yields full container compromise. Affected: all versions < 4.6.78. Fixed: 4.6.78+. CWEs: CWE-22 (Path Traversal) and CWE-78 (OS Command Injection).

The vendor CVSS of 9.9 CRITICAL is technically defensible *in isolation*—Scope: Changed, low privileges, no user interaction, full CIA impact. But it overstates risk for any enterprise patching queue because PraisonAI is a niche developer tool with ~66k monthly PyPI downloads and ~9k GitHub stars, not a canonical infrastructure component. The attack surface requires authenticated access to the AICoder chat interface and relies on prompt injection as the exploitation vector—a probabilistic technique mediated by an LLM, not a deterministic HTTP request. In virtually all real deployments, PraisonAI sits on a developer laptop or an internal sandbox container that never faces the internet. The 9.9 paints this as Log4Shell-adjacent; reality is closer to a severe local-dev-tool bug that most SOCs will never encounter.

"Niche AI dev tool, nasty root RCE—but your fleet probably doesn't run it."
02 · The Attack Path

4 steps from start to impact.

STEP 01

Reach the AICoder chat interface

The attacker must have network access to the PraisonAI web UI and at minimum a low-privilege account (the CVSS vector specifies PR:L). In default Docker deployments, PraisonAI binds to 0.0.0.0:8080 inside the container; if port-forwarded or exposed via a reverse proxy, it becomes reachable. Prior to the separate fix for CVE-2026-44338, authentication was disabled by default (AUTH_ENABLED = False), which would have made this step trivial.
Conditions required:
  • Network path to PraisonAI AICoder UI (typically port 8080)
  • Valid session or authentication disabled (pre-CVE-2026-44338 default)
Where this breaks in practice:
  • PraisonAI is overwhelmingly deployed on developer laptops or ephemeral Docker containers, not internet-facing servers
  • ~66k monthly PyPI downloads across mostly individual developers—enterprise fleet prevalence is near-zero
  • Even on internal networks, a random developer's Docker container is unlikely to be reachable from other VLANs
Detection/coverage: Asset inventory or pip list audit across hosts will surface PraisonAI. Shodan/Censys queries for PraisonAI-specific paths (/api/agents, /docs) may reveal external exposure, but expect very low hit counts.
STEP 02

Craft a prompt-injection payload

The attacker composes a chat message designed to manipulate the underlying LLM into invoking the write_to_file or execute_command tool calls with attacker-controlled arguments. This is an indirect prompt injection vector: the attacker's message is the user turn, and the LLM decides whether to honor it by calling the tool. Payload reliability depends on the LLM model, system prompt, and temperature settings. No weaponized tooling or named exploit framework is publicly available for this CVE.
Conditions required:
  • Knowledge that AICoder exposes write_to_file and execute_command tools
  • Ability to submit chat messages to the AICoder interface
Where this breaks in practice:
  • Prompt injection is probabilistic, not deterministic—the LLM may refuse, hallucinate, or misinterpret the payload
  • No public PoC or exploit script exists as of October 2026
  • Different LLM backends (GPT-4, Claude, Llama) will respond differently to the same injection, reducing reliability
Detection/coverage: LLM observability platforms (Langfuse, Helicone, Arthur) or custom logging on tool-call invocations would surface suspicious write_to_file/execute_command calls with unexpected paths.
STEP 03

Trigger arbitrary file write via path traversal

If the LLM honors the injected prompt, it calls write_to_file with an absolute path like /root/.ssh/authorized_keys or /etc/crontab. The apply_llm_response handler uses os.path.join(workspace, attacker_path) which Python resolves to the absolute path when the second argument starts with /, completely bypassing the intended workspace restriction. The file is written with root permissions inside the container.
Conditions required:
  • Successful prompt injection from Step 2
  • Container running as root (Docker default for PraisonAI)
Where this breaks in practice:
  • If the container uses a read-only filesystem or non-root user, writes to sensitive paths will fail
  • AppArmor/SELinux profiles on the host may restrict container writes to mounted volumes only
Detection/coverage: File integrity monitoring (AIDE, osquery, Wazuh) on the host. Container runtime security tools (Falco, Sysdig Secure) will alert on writes to /etc/crontab, /root/.ssh/, etc.
STEP 04

Execute arbitrary commands as root

Alternatively or additionally, the LLM calls execute_command with attacker-supplied shell commands. The function passes the string directly to the shell with no allowlist, no blocklist, and no sandboxing. The attacker achieves a root shell inside the container—reverse shells, crypto miners, data exfiltration, or lateral movement tooling can be deployed. From here, the attacker can probe the Docker socket, host mounts, and network neighbors.
Conditions required:
  • Successful prompt injection from Step 2
  • execute_command tool available and unsanitized
Where this breaks in practice:
  • Container network segmentation may prevent outbound connections for reverse shells
  • If the Docker socket is not mounted (best practice), container-to-host escape requires a separate kernel or runtime exploit
  • Blast radius is typically one container, not fleet-wide
Detection/coverage: Container runtime alerts (Falco) on unexpected process execution. EDR on the host may detect reverse shell patterns. Network monitoring for C2 callbacks from container IP ranges.
03 · Compensating Control

1
HIGH 7.2→IGNORE 0.0
SEVERITY REDUCED
Upgrade PraisonAI to 4.6.78+ immediately — The definitive fix. Run pip install --upgrade praisonai>=4.6.78 on every host where it is installed. Version 4.6.78 adds path validation rejecting absolute paths and .. traversals in write_to_file, plus command allowlisting in execute_command. As a HIGH-severity finding, the noisgate remediation SLA is ≤ 180 days, but given this is a single pip install command, there is no reason to delay. Deploy within 30 days per the noisgate mitigation SLA.
2
HIGH 7.2→IGNORE 0.0
SEVERITY REDUCED
Disable or remove the AICoder UI component — If upgrade is not immediately possible, disable the AICoder feature entirely. Remove the AICoder route from the PraisonAI configuration or stop the web server component. This eliminates the entire attack surface (no chat UI → no prompt injection → no tool calls). Deploy within 30 days per noisgate mitigation SLA for HIGH.
3
HIGH 7.2→LOW 2.5
SEVERITY REDUCED
Network-isolate PraisonAI instances to localhost only — Bind PraisonAI to 127.0.0.1 instead of 0.0.0.0. If running in Docker, do not publish the port (-p flag) or restrict it to the loopback interface (-p 127.0.0.1:8080:8080). This reduces the attack surface to local-only access, eliminating remote network exploitation. Even if prompt injection succeeds locally, the attacker must already have shell access to the host, which makes the vuln redundant.
4
HIGH 7.2→MEDIUM 5.0
SEVERITY REDUCED
Run PraisonAI containers as non-root with read-only filesystem — Add --user 1000:1000 and --read-only flags to the Docker run command. This prevents writes to sensitive paths like /etc/crontab and /root/.ssh/ even if path traversal succeeds, and limits execute_command to non-root privilege. Combined with dropping all capabilities (--cap-drop=ALL), the blast radius of successful exploitation drops dramatically.
5
HIGH 7.2→HIGH 6.5
Deploy container runtime security (Falco/Sysdig) — Configure Falco rules to alert on unexpected file writes to /etc/, /root/.ssh/, and unexpected process execution inside PraisonAI containers. This does not prevent exploitation but provides detection and response capability, reducing dwell time. Useful as a defense-in-depth layer alongside the controls above.
What doesn't work
  • WAF in front of PraisonAI — The attack payload is a natural-language prompt, not a SQL injection or XSS pattern. WAF signatures cannot distinguish a malicious AI prompt from a legitimate one without deep LLM-aware inspection, which no mainstream WAF offers today.
  • Network-level IDS/IPS (Snort/Suricata) — The prompt injection travels over a normal HTTPS POST to the chat endpoint. There is no exploit signature to match; the malicious content is semantic, not syntactic.
  • Pinning the LLM system prompt with safety instructions — While adding 'never write to /etc' to the system prompt may reduce success rate, prompt injection techniques routinely bypass system prompt constraints. This is not a reliable compensating control.
04 · Intelligence Metadata

The supporting signals.

In-the-wild exploitationNot observed. Not on CISA KEV. No known campaigns targeting this CVE. GreyNoise observed scanning for PraisonAI instances within 4 hours of a *related* CVE (CVE-2026-44338, auth bypass) disclosure in May 2026, indicating attacker awareness of the project, but no exploitation telemetry for CVE-2026-61445 specifically.
Proof-of-concept availabilityNone public. No PoC on pocindex.io, no GitHub repos named after CVE-2026-61445, no Nuclei templates, no ExploitDB or Metasploit modules. The attack is conceptually simple (prompt injection → tool call) but no weaponized script exists.
EPSS score0.00881 (0.881% probability of exploitation in 30 days), approximately 82nd percentile. Low exploitability signal from the model.
CISA KEV statusNot listed. No federal mandate for remediation timelines.
CVSS vector analysisCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H — The Scope: Changed flag drives the 9.9. This assumes the attacker escapes the vulnerable component's authorization scope (e.g., container → host). In practice, most Docker deployments without socket mounts do NOT grant host-level scope change, making S:C optimistic.
Affected versionsAll PraisonAI versions < 4.6.78 (PyPI package praisonai). Current latest is 4.7.13.
Fixed version4.6.78 — adds path validation to write_to_file (rejects absolute paths and .. traversals) and command sanitization/allowlisting to execute_command. No distro backports; PraisonAI is pip-only.
Install base / exposure~1.6M total PyPI downloads, ~66k/month, ~9k GitHub stars. Developer-centric, individual-contributor adoption pattern. No evidence of significant enterprise fleet deployment. GreyNoise/Shodan exposure data for PraisonAI instances is minimal—this is not an edge appliance.
Disclosure date2026-07-11 via GitHub Security Advisory GHSA-9mp3-24cc-77mg.
Reporting researcherNot publicly credited in the GHSA, NVD, or vendor advisory. Discovered via the PraisonAI project's own security advisory process.

Sources.

  1. GitHub Security Advisory GHSA-9mp3-24cc-77mg
  2. NVD — CVE-2026-61445
  3. GitLab Advisory Database — CVE-2026-61445
  4. OSV — GHSA-9mp3-24cc-77mg
  5. CSO Online — PraisonAI vulnerability scanning within 4 hours
  6. PraisonAI PyPI download stats (pepy.tech)
  7. PraisonAI GitHub Repository
  8. Strix AI — CVE-2026-61445 Summary
05 · The Call

Final Verdict
↓ DOWNGRADED to HIGH (7.2/10)

Why this verdict

  • Minuscule enterprise footprint: PraisonAI has ~66k monthly PyPI downloads, overwhelmingly individual developers and researchers. An enterprise fleet of 10,000 hosts is statistically unlikely to have *any* PraisonAI installations. The vendor CVSS of 9.9 treats this as ubiquitous infrastructure; it is not.
  • Prompt injection is a probabilistic attack vector: Unlike a crafted HTTP request that deterministically triggers a buffer overflow, prompt injection depends on the LLM model, system prompt, temperature, and alignment guardrails. Exploitation reliability is significantly lower than a traditional RCE, warranting AC:H-equivalent friction even though the CVSS formally marks AC:L.
  • Blast radius capped at one container/host: Even successful exploitation yields root inside a single Docker container. Without a mounted Docker socket or kernel exploit, there is no lateral movement to the host or fleet. This is not a domain controller, hypervisor, or CI/CD server—it is a developer sandbox.
  • No PoC, no KEV, low EPSS: Zero public exploit code, not on CISA KEV, EPSS at 0.881%. The threat intelligence signal is uniformly cold.
  • Role multiplier: (a) *Developer workstation/sandbox* (>95% of installs): chain succeeds, blast radius = one container. Severity remains HIGH for that host but does not propagate. (b) *Internal AI automation server* (<5%): chain succeeds if chat UI is reachable on internal network, blast radius = one server, possible data on that host. (c) *CI/CD pipeline integration* (<1% of installs): theoretically possible if AICoder is embedded in a build workflow, which would elevate blast radius to supply-chain. However, PraisonAI is NOT a canonical CI/CD component—it is an AI agent framework. No evidence of CI/CD adoption pattern. This role does not meet the ≥1% threshold to set a floor.
  • Attacker interest exists for PraisonAI broadly: GreyNoise documented scanning for PraisonAI endpoints within 4 hours of a related CVE disclosure (CVE-2026-44338). While that was a different bug, it confirms adversary awareness of the project—preventing a full downgrade to MEDIUM.

Why not higher?

A CRITICAL rating would require either a canonical high-value deployment role (this is a developer tool, not a DC/hypervisor/CI server) or evidence of active exploitation (none exists). The probabilistic nature of prompt injection, the near-zero enterprise footprint, and the container-scoped blast radius all argue against CRITICAL. The 9.9 vendor score is a theoretical maximum that doesn't reflect how this software actually lives in production environments.

Why not lower?

Root-level RCE is root-level RCE, even in a container. The technical exploit chain is short (two steps from chat access to shell), the fix was only released three months ago so many pip installs may still be vulnerable, and GreyNoise data shows real attacker interest in PraisonAI as a target class. Any developer running an unpatched instance with the AICoder UI exposed—even on an internal network—faces genuine compromise risk. MEDIUM would understate the severity for the (small) population actually running this tool.

06 · Verification

Crowdsourced verification payload.

Run this script on any host where PraisonAI might be installed. Requires no special privileges—just the ability to run python3. Invoke with: python3 check_cve_2026_61445.py. The script checks the installed praisonai package version and reports VULNERABLE, PATCHED, or UNKNOWN.

noisgate-verify.py
PYTHONREAD-ONLYSAFE
#!/usr/bin/env python3
"""Check for CVE-2026-61445: PraisonAI AICoder arbitrary file write + RCE.
Fixed in version 4.6.78.
Exit codes: 1 = VULNERABLE, 0 = PATCHED, 2 = UNKNOWN
"""
import sys

try:
    from importlib.metadata import version as get_version, PackageNotFoundError
except ImportError:
    from importlib_metadata import version as get_version, PackageNotFoundError

FIXED_VERSION = (4, 6, 78)

def parse_version(v):
    """Parse a PEP-440 version string into a comparable tuple."""
    parts = []
    for segment in v.split('.'):
        # strip pre/post/dev suffixes for comparison
        num = ''.join(c for c in segment if c.isdigit())
        if num:
            parts.append(int(num))
    return tuple(parts)

try:
    installed = get_version('praisonai')
except PackageNotFoundError:
    print('UNKNOWN — praisonai package is not installed on this host.')
    sys.exit(2)

parsed = parse_version(installed)

if not parsed:
    print(f'UNKNOWN — could not parse version string: {installed}')
    sys.exit(2)

if parsed < FIXED_VERSION:
    print(f'VULNERABLE — praisonai {installed} is installed (fixed in 4.6.78).')
    sys.exit(1)
else:
    print(f'PATCHED — praisonai {installed} is installed (>= 4.6.78).')
    sys.exit(0)