VAITP Dataset

← Back to the dataset

CVE-2026-47392

PraisonAI sandbox bypass using `print.__self__` allows OS command execution.

  • CVSS 9.9
  • CWE-184
  • Design Defects
  • Remote

PraisonAI is a multi-agent teams system. Prior to version 4.6.40 of PraisonAI, corresponding to version 1.6.40 of praisonaiagents, `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). PraisonAI version 4.6.40 and praisonaiagents version 1.6.40 contain an updated fix.

CVSS base score
9.9
Published
2026-07-21
OWASP
A03 Injection
Orthogonal defect classification
Checking
Code defect classification
Incorrect Check
Category
Design Defects
Subcategory
Command Injection
Accessibility scope
Remote
Impact
Arbitrary Code Execution
Affected component
PraisonAI
Fixed by upgrading
Yes

Solution

Upgrade PraisonAI to version 4.6.40 and praisonaiagents to version 1.6.40 or later.

Vulnerable code sample

import subprocess
import sys

def execute_code(code: str):
    """
    Executes Python code in a supposedly sandboxed environment.
    This function simulates the vulnerable state before the patch.
    """
    # A restricted set of built-ins is created to limit capabilities.
    # However, it fails to prevent access to the original builtins module
    # through attributes of the whitelisted functions.
    safe_builtins = {
        'print': print,
        'len': len,
        'str': str,
        'int': int,
        'vars': vars,  # vars() is a key part of the described exploit path.
        'list': list,
        'dict': dict,
    }

    restricted_globals = {"__builtins__": safe_builtins}

    try:
        # The vulnerable call to exec. The restricted scope can be bypassed using
        # an escape like `print.__self__` to retrieve the full builtins module,
        # from which `__import__` can be accessed.
        exec(code, restricted_globals, {})
    except Exception as e:
        print(f"Code execution resulted in an error: {e}", file=sys.stderr)

Patched code sample

import builtins
import io
import sys

def execute_code(code: str) -> str:
    """
    Represents the fixed version of the code execution tool.
    The vulnerability is patched by wrapping the `print` function.
    """
    
    # This wrapper function does not have a `__self__` attribute that
    # points back to the `builtins` module, breaking the exploit chain.
    def safe_print_wrapper(*args, **kwargs):
        return builtins.print(*args, **kwargs)

    # The sandboxed environment's builtins are carefully selected.
    # The original `builtins.print` is replaced with the safe wrapper.
    safe_builtins = {
        'print': safe_print_wrapper,  # <-- THE FIX
        'vars': builtins.vars,
        'str': builtins.str,
        'int': builtins.int,
        'list': builtins.list,
        'dict': builtins.dict,
    }

    restricted_globals = {"__builtins__": safe_builtins}
    
    # Capture output to return as a string
    old_stdout = sys.stdout
    redirected_output = io.StringIO()
    sys.stdout = redirected_output
    
    try:
        # An attacker trying the payload would now get an AttributeError
        # because `safe_print_wrapper` does not have a `__self__` attribute.
        # Payload: "print(vars(print.__self__)['__import__']('os').system('echo pwned'))"
        exec(code, restricted_globals, {})
    except Exception as e:
        # The expected failure for the exploit is an AttributeError.
        print(f"Execution failed: {e}")
    finally:
        sys.stdout = old_stdout
        
    return redirected_output.getvalue()

Payload

vars(print.__self__)['__impo'+'rt__']('os').system('id')

Cite this entry

@misc{vaitp:cve202647392,
  title        = {{PraisonAI sandbox bypass using `print.__self__` allows OS command execution.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2026},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2026-47392},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-47392/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.

The supreme art of war is to subdue the enemy without fighting.

Sun Tzu – “The Art of War”

:: Shaping the future through research and ingenuity ::