GHSA-pv2j-rghr-v5r9: praisonaiagents: sandbox escape via format-spec read

GHSA-pv2j-rghr-v5r9 MEDIUM
Published June 18, 2026
CISO Take

PraisonAI Agents' code-execution sandbox is bypassed through two combined CPython mechanics: runtime string assembly fools the AST validator into missing blocked dunder names, while C-level attribute resolution via str.format/str.format_map sidesteps the Python-level _safe_getattr guard entirely — yielding an arbitrary read primitive over __class__, __globals__, __dict__, and every other blocked attribute. The critical-risk approval gate (@require_approval) is automatically disabled whenever PRAISONAI_AUTO_APPROVE is set, a condition triggered by FULL_AUTO mode, CI workflows, and bot launchers, meaning any LLM-visible surface — user input, RAG-retrieved document, MCP tool output — can deliver and execute the payload with zero human intervention. With a callable bridge, the current read primitive escalates to in-process code execution inside an unfiltered subprocess that has no seccomp, no setrlimit, and no syscall filtering; 11 downstream dependents and 42 prior CVEs in the same package suggest a pattern of weak sandbox assumptions that compounds this risk. Organizations using praisonaiagents in autonomous pipelines must patch to 1.6.59 immediately and audit every deployment for PRAISONAI_AUTO_APPROVE usage.

Sources: GitHub Advisory ATLAS NVD

What is the risk?

The CVSS 6.5 Medium rating materially understates operational risk in agentic deployments. The auto-approve bypass converts a chained-exploit sandbox escape into a single-step attack whenever FULL_AUTO mode is active — a common posture for CI-integrated or fully-automated agent pipelines. The read primitive is confirmed with a published PoC; an execution primitive is plausible given the subprocess lacks syscall filtering. The attack requires only Low privileges per the CVSS vector and no user interaction, and the format-spec vector is not blocked in sandbox mode at all. With 42 prior CVEs in the same package, this codebase has a demonstrated history of security boundary failures. Effective risk in production agentic systems should be assessed as HIGH.

How does the attack unfold?

Payload Injection
Attacker embeds malicious Python code in LLM-visible content — a RAG-indexed document, web-scraped page, or poisoned MCP tool response — knowing the autonomous agent will process it and pass it to execute_code.
AML.T0051.001
Sandbox Bypass
Payload assembles blocked dunder names at runtime (e.g., '_'*2+'class'+'_'*2) to evade AST validation, then uses str.format_map to resolve __class__.__globals__ through CPython's C-level attribute path, bypassing _safe_getattr entirely.
AML.T0107
Runtime Data Exfiltration
Attacker reads __globals__ and __dict__ from the running agent process to extract loaded modules, environment variables, API keys, and database credentials present in process memory.
AML.T0053
Escalation to Execution
Using a callable bridge discovered via the object graph traversal, attacker escalates from read primitive to arbitrary command execution in the unfiltered subprocess, enabling lateral movement, data destruction, or persistent access.
AML.T0105

What systems are affected?

Package Ecosystem Vulnerable Range Patched
PraisonAI Agents pip < 1.6.59 1.6.59
11 dependents 65% patched ~6d to patch Full package profile →

Do you use PraisonAI Agents? You're affected.

How severe is it?

CVSS 3.1
6.5 / 10
EPSS
N/A
Exploitation Status
No known exploitation
Sophistication
Moderate

What is the attack surface?

AV AC PR UI S C I A
AV Network
AC Low
PR Low
UI None
S Unchanged
C High
I None
A None

What should I do?

6 steps
  1. Patch immediately: upgrade praisonaiagents to >= 1.6.59 which addresses the format/format_map bypass.

  2. Remove PRAISONAI_AUTO_APPROVE=true from all production and CI environments — treat it as a security-critical configuration setting, not a convenience flag; document all places it is set.

  3. Replace the in-process sandbox with genuine OS-level isolation (gVisor, firejail, container, or microVM) for any deployment executing untrusted code — the current blocklist-based design cannot be fully secured by additions alone.

  4. Until patched, add 'format' and 'format_map' to _SANDBOX_BLOCKED_CALLS in python_tools.py:56-60 as a partial workaround.

  5. Audit RAG sources, MCP tool outputs, web-scraped content, and any user-controlled text flowing into execute_code for runtime string assembly patterns such as '_'*2 + 'class' + '_'*2.

  6. Review all CI pipelines that set PRAISONAI_AUTO_APPROVE and consider introducing a human-approval step before code execution in production contexts.

How is it classified?

Which compliance frameworks are affected?

This CVE is relevant to:

EU AI Act
Article 15 - Accuracy, robustness and cybersecurity Article 9 - Risk management system
ISO 42001
6.1.2 - AI risk assessment 8.4 - AI risk treatment
NIST AI RMF
MANAGE 2.2 - Mechanisms are in place and applied to sustain the value and benefits of the AI system
OWASP LLM Top 10
LLM01 - Prompt Injection LLM06 - Excessive Agency

Frequently Asked Questions

What is GHSA-pv2j-rghr-v5r9?

PraisonAI Agents' code-execution sandbox is bypassed through two combined CPython mechanics: runtime string assembly fools the AST validator into missing blocked dunder names, while C-level attribute resolution via str.format/str.format_map sidesteps the Python-level _safe_getattr guard entirely — yielding an arbitrary read primitive over __class__, __globals__, __dict__, and every other blocked attribute. The critical-risk approval gate (@require_approval) is automatically disabled whenever PRAISONAI_AUTO_APPROVE is set, a condition triggered by FULL_AUTO mode, CI workflows, and bot launchers, meaning any LLM-visible surface — user input, RAG-retrieved document, MCP tool output — can deliver and execute the payload with zero human intervention. With a callable bridge, the current read primitive escalates to in-process code execution inside an unfiltered subprocess that has no seccomp, no setrlimit, and no syscall filtering; 11 downstream dependents and 42 prior CVEs in the same package suggest a pattern of weak sandbox assumptions that compounds this risk. Organizations using praisonaiagents in autonomous pipelines must patch to 1.6.59 immediately and audit every deployment for PRAISONAI_AUTO_APPROVE usage.

Is GHSA-pv2j-rghr-v5r9 actively exploited?

No confirmed active exploitation of GHSA-pv2j-rghr-v5r9 has been reported, but organizations should still patch proactively.

How to fix GHSA-pv2j-rghr-v5r9?

1. Patch immediately: upgrade praisonaiagents to >= 1.6.59 which addresses the format/format_map bypass. 2. Remove PRAISONAI_AUTO_APPROVE=true from all production and CI environments — treat it as a security-critical configuration setting, not a convenience flag; document all places it is set. 3. Replace the in-process sandbox with genuine OS-level isolation (gVisor, firejail, container, or microVM) for any deployment executing untrusted code — the current blocklist-based design cannot be fully secured by additions alone. 4. Until patched, add 'format' and 'format_map' to _SANDBOX_BLOCKED_CALLS in python_tools.py:56-60 as a partial workaround. 5. Audit RAG sources, MCP tool outputs, web-scraped content, and any user-controlled text flowing into execute_code for runtime string assembly patterns such as '_'*2 + 'class' + '_'*2. 6. Review all CI pipelines that set PRAISONAI_AUTO_APPROVE and consider introducing a human-approval step before code execution in production contexts.

What systems are affected by GHSA-pv2j-rghr-v5r9?

This vulnerability affects the following AI/ML architecture patterns: Agent frameworks, Autonomous AI pipelines, Code execution sandboxes, LLM-integrated CI/CD, RAG pipelines.

What is the CVSS score for GHSA-pv2j-rghr-v5r9?

GHSA-pv2j-rghr-v5r9 has a CVSS v3.1 base score of 6.5 (MEDIUM).

What is the AI security impact?

Affected AI Architectures

Agent frameworksAutonomous AI pipelinesCode execution sandboxesLLM-integrated CI/CDRAG pipelines

MITRE ATLAS Techniques

AML.T0050 Command and Scripting Interpreter
AML.T0051.001 Indirect
AML.T0053 AI Agent Tool Invocation
AML.T0080 AI Agent Context Poisoning
AML.T0105 Escape to Host
AML.T0107 Exploitation for Defense Evasion

Compliance Controls Affected

EU AI Act: Article 15, Article 9
ISO 42001: 6.1.2, 8.4
NIST AI RMF: MANAGE 2.2
OWASP LLM Top 10: LLM01, LLM06

What are the technical details?

Original Advisory

## Summary The `execute_code` tool's subprocess sandbox advertises a three-layer defense (AST validation, text-pattern blocklist, restricted `__builtins__`). In **sandbox mode** (the default) only two layers are active — the text-pattern blocklist is skipped — and both remaining layers are bypassed by combining two CPython semantics: 1. **Runtime string assembly.** The AST validator (`src/praisonai-agents/praisonaiagents/tools/python_tools.py:75`) enumerates blocked dunder names against `ast.Attribute.attr`, `ast.Call.func.id`, and `ast.Constant` string-substring. Names assembled at runtime (e.g. `"_"*2 + "class" + "_"*2`) appear in the AST as multiple short `ast.Constant` nodes, none containing a blocked substring, so the static check passes. 2. **C-level attribute access via format-spec.** `str.format` / `str.format_map` resolve dotted field references through CPython's internal `PyObject_GetAttr` (`do_string_format` → `get_field`). This C path never consults the Python-level `getattr` binding. The sandbox's `_safe_getattr` wrapper (`python_tools.py:221`) is installed only as the `getattr` name in `safe_builtins`, so any C-level attribute access — including format-spec field resolution — sidesteps it. `format`/`format_map` are also absent from `_SANDBOX_BLOCKED_CALLS` (`python_tools.py:56`). Combined, this yields an arbitrary read primitive over every blocklisted attribute (`__class__`, `__qualname__`, `__bases__`, `__base__`, function `__globals__`, `__dict__`, …). ## Affected code | File | Lines | Symbol | Role | |---|---|---|---| | `src/praisonai-agents/praisonaiagents/tools/python_tools.py` | 39–54 | `_SANDBOX_BLOCKED_ATTRS` | The blocklist the bypass renders unreachable | | `src/praisonai-agents/praisonaiagents/tools/python_tools.py` | 56–60 | `_SANDBOX_BLOCKED_CALLS` | Missing entries: `format`, `format_map` | | `src/praisonai-agents/praisonaiagents/tools/python_tools.py` | 75–102 | `_validate_code_ast` | Static check, blind to runtime string assembly | | `src/praisonai-agents/praisonaiagents/tools/python_tools.py` | 221–226 | `_safe_getattr` | Wraps Python-level `getattr` only; C-level access bypasses | | `src/praisonai-agents/praisonaiagents/tools/python_tools.py` | 352 | `execute_code` | Entry point; gated by `@require_approval(risk_level="critical")` | ## Reproducer ```python import os os.environ["PRAISONAI_AUTO_APPROVE"] = "true" from praisonaiagents.tools.python_tools import execute_code payload = ''' und = "_" * 2 # "__" assembled at runtime key1 = und + "class" + und # "__class__" key2 = und + "qualname" + und # "__qualname__" fmt_class = "{0." + key1 + "}" fmt_qual2 = "{0." + key1 + "." + key2 + "}" print("LEAK_CLASS=" + fmt_class.format(())) print("LEAK_QUAL2=" + fmt_qual2.format(())) ''' print(execute_code(payload, sandbox_mode="sandbox", timeout=15)) ``` Observed: `stdout` = `LEAK_CLASS=<class 'tuple'>` / `LEAK_QUAL2=tuple`, `success: true`, no security error. Both `__class__` (one hop) and `__class__.__qualname__` (two hops) — all blocklisted — are read. ## Trust boundary The `@require_approval(risk_level="critical")` gate is bypassed when `PRAISONAI_AUTO_APPROVE` is set (verified: `require_approval` short-circuits on `is_env_auto_approve()`). That variable is set by the project's FULL_AUTO autonomy mode, the bots-CLI launchers, and the project's own issue-triage CI workflow — postures where the agent reaches `execute_code` with no human approval. The payload then arrives via any LLM-visible surface (user message, retrieved document, tool/web/MCP output) and the tool-call machinery passes it as the `code` argument. ## Relationship to GHSA-4mr5-g6f9-cfrh The code's own comment at `python_tools.py:46` cites GHSA-4mr5-g6f9-cfrh, which added `__self__` to the blocklist to stop C-builtins leaking `builtins` via `func.__self__`. This finding does not bypass that single entry — it bypasses the **entire** blocklist, because format-spec attribute resolution never consults the blocklist or `_safe_getattr`. `"{0.__self__}".format(print)` would leak `__self__` regardless of the blocklist. Same defense surface, different mechanism; the GHSA-4mr5 fix does not mitigate this. ## Scope (read primitive only) This reports the **read primitive**. Turning the read into in-process execution requires a callable bridge; the obvious one (`string.Formatter().get_field()` returning the live object) is not directly reachable because `import string` is blocked at the AST layer (no `ast.Import`). Other bridges may exist; a full execution chain is **not** claimed here. If one is found, severity rises to ~8.8 (the subprocess has no seccomp/`setrlimit`/syscall filtering). ## Suggested fix 1. Add `format`, `format_map` to `_SANDBOX_BLOCKED_CALLS` (blocks the calls at the AST layer; cost: also blocks benign `str.format`). 2. Or replace `str` in `safe_builtins` with a subclass whose `format`/`format_map` reject dotted fields resolving to leading-underscore attributes (preserves benign formatting). 3. Or drop sandbox-mode's in-process security claim and document that real isolation requires external sandboxing (gVisor/firejail/container/microVM) — which matches what the subprocess provides today. The text-pattern blocklist present in the `direct` path (`python_tools.py:487-502`) is absent from the sandbox path; even if added, the runtime-assembly trick defeats it, so (1) or (2) is required. Reporter: Kai Aizen / SnailSploit — kai@snailsploit.com — PGP on request. Coordinated disclosure; no public posting.

Exploitation Scenario

An attacker embeds a Python sandbox-escape payload inside a document indexed in a RAG knowledge base, a web page scraped during autonomous research, or a poisoned MCP tool response. When the LLM processes this content and calls execute_code — automatically in FULL_AUTO mode or CI environments where PRAISONAI_AUTO_APPROVE is set — the payload assembles blocked dunder names at runtime (e.g., '_'*2+'class'+'_'*2) to pass AST validation undetected, then invokes str.format_map with a dotted field reference like {0.__class__.__globals__} to resolve attributes through CPython's C-level path, entirely bypassing _safe_getattr. The attacker reads imported modules and environment variables from __globals__, extracting API keys, database credentials, or authentication tokens loaded into the agent process. With a callable bridge reachable from the object graph — for instance via a module reference discovered in __globals__ — the attacker escalates from read primitive to arbitrary command execution within the unfiltered subprocess, enabling data exfiltration, lateral movement, or destruction of downstream systems.

Weaknesses (CWE)

CWE-693 — Protection Mechanism Failure: The product does not use or incorrectly uses a protection mechanism that provides sufficient defense against directed attacks against the product.

Source: MITRE CWE corpus.

CVSS Vector

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N

Timeline

Published
June 18, 2026
Last Modified
June 18, 2026
First Seen
June 18, 2026

Related Vulnerabilities