Логотип exploitDog
Консоль
Логотип exploitDog

exploitDog

github логотип

GHSA-pv2j-rghr-v5r9

Опубликовано: 18 июн. 2026
Источник: github
Github: Прошло ревью
CVSS3: 6.5

Описание

PraisonAI: execute_code sandbox bypass: str.format C-level attribute access reads every blocklisted dunder

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_formatget_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

FileLinesSymbolRole
src/praisonai-agents/praisonaiagents/tools/python_tools.py39–54_SANDBOX_BLOCKED_ATTRSThe blocklist the bypass renders unreachable
src/praisonai-agents/praisonaiagents/tools/python_tools.py56–60_SANDBOX_BLOCKED_CALLSMissing entries: format, format_map
src/praisonai-agents/praisonaiagents/tools/python_tools.py75–102_validate_code_astStatic check, blind to runtime string assembly
src/praisonai-agents/praisonaiagents/tools/python_tools.py221–226_safe_getattrWraps Python-level getattr only; C-level access bypasses
src/praisonai-agents/praisonaiagents/tools/python_tools.py352execute_codeEntry point; gated by @require_approval(risk_level="critical")

Reproducer

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.

Пакеты

Наименование

praisonaiagents

pip
Затронутые версииВерсия исправления

< 1.6.59

1.6.59

EPSS

Процентиль: 25%
0.00318
Низкий

6.5 Medium

CVSS3

Дефекты

CWE-693

Связанные уязвимости

CVSS3: 6.5
nvd
5 дней назад

PraisonAI is a multi-agent teams system. Prior to praisonaiagents 1.6.59, execute_code sandbox mode permits runtime assembly of blocklisted dunder names and allows str.format or str.format_map to resolve dotted fields through C-level attribute access that bypasses _safe_getattr. This exposes class, qualified-name, base-class, globals, and object-dictionary attributes to prompt-influenced code when approval is automatically granted, producing a high-impact read primitive without establishing a complete in-process execution chain. This issue is fixed in praisonaiagents 1.6.59.

EPSS

Процентиль: 25%
0.00318
Низкий

6.5 Medium

CVSS3

Дефекты

CWE-693