CVE-2026-39888
PraisonAIAgents has a sandbox escape via exception frame traversal in `execute_code` (subprocess mode)
PraisonAI is a multi-agent teams system. Prior to 1.5.115, execute_code() in praisonaiagents.tools.python_tools defaults to sandbox_mode="sandbox", which runs user code in a subprocess wrapped with a restricted builtins dict and an AST-based blocklist. The AST blocklist embedded inside the subprocess wrapper (blocked_attrs of python_tools.py) contains only 11 attribute names — a strict subset of the 30+ names blocked in the direct-execution path. The four attributes that form a frame-traversal chain out of the sandbox are all absent from the subprocess list (traceback, tb_frame, f_back, and f_builtins). Chaining these attributes through a caught exception exposes the real Python builtins dict of the subprocess wrapper frame, from which exec can be retrieved and called under a non-blocked variable name — bypassing every remaining security layer. This vulnerability is fixed in 1.5.115.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- Low
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- High
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
10.0
PraisonAIAgents has a sandbox escape via exception frame traversal in `execute_code` (subprocess mode)
PraisonAI is a multi-agent teams system. Prior to 1.5.115, execute_code() in praisonaiagents.tools.python_tools defaults to sandbox_mode="sandbox", which runs user code in a subprocess wrapped with a restricted builtins dict and an AST-based blocklist. The AST blocklist embedded inside the subprocess wrapper (blocked_attrs of python_tools.py) contains only 11 attribute names — a strict subset of the 30+ names blocked in the direct-execution path. The four attributes that form a frame-traversal chain out of the sandbox are all absent from the subprocess list (traceback, tb_frame, f_back, and f_builtins). Chaining these attributes through a caught exception exposes the real Python builtins dict of the subprocess wrapper frame, from which exec can be retrieved and called under a non-blocked variable name — bypassing every remaining security layer. This vulnerability is fixed in 1.5.115.
- Record
CVE-2026-39888- CVSS 3.1
- 10.0 Critical
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H - Weakness
- CWE-657, CWE-693
- Updated
- 2026-11-02
EUVDEUVD-2026-20635
10.0
PraisonAI is a multi-agent teams system. Prior to 1.5.115, execute_code() in praisonaiagents.tools.python_tools defaults to sandbox_mode="sandbox", which runs user code in a subprocess wrapped with a restricted builtins dict and an AST-based blocklist. The AST blocklist embedded inside the subprocess wrapper (blocked_attrs of python_tools.py) contains only 11 attribute names — a strict subset of the 30+ names blocked in the direct-execution path. The four attributes that form a frame-traversal chain out of the sandbox are all absent from the subprocess list (traceback, tb_frame, f_back, and f_builtins). Chaining these attributes through a caught exception exposes the real Python builtins dict of the subprocess wrapper frame, from which exec can be retrieved and called under a non-blocked variable name — bypassing every remaining security layer. This vulnerability is fixed in 1.5.115.
- Record
EUVD-2026-20635- CVSS 3.1
- 10.0 Critical
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-qf73-2hrx-xprp
9.9
PraisonAI has sandbox escape via exception frame traversal in `execute_code` (subprocess mode)
Summary
execute_code() in praisonaiagents.tools.python_tools defaults to
sandbox_mode="sandbox", which runs user code in a subprocess wrapped with a
restricted __builtins__ dict and an AST-based blocklist. The AST blocklist
embedded inside the subprocess wrapper (blocked_attrs, line 143 of
python_tools.py) contains only 11 attribute names — a strict subset of the 30+
names blocked in the direct-execution path. The four attributes that form a
frame-traversal chain out of the sandbox are all absent from the subprocess list:
| Attribute | In subprocess blocked_attrs | In direct-mode _blocked_attrs |
|---|---|---|
| __traceback__ | NO | YES |
| tb_frame | NO | YES |
| f_back | NO | YES |
| f_builtins | NO | YES |
Chaining these attributes through a caught exception exposes the real Python
builtins dict of the subprocess wrapper frame, from which exec can be
retrieved and called under a non-blocked variable name — bypassing every
remaining security layer.
Tested and confirmed on praisonaiagents 1.5.113 (latest), Python 3.10.
Severity
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H — 9.9 Critical
| Vector | Value | Rationale |
|---|---|---|
| AV:N | Network | execute_code is a designated agent tool; user/LLM-supplied code reaches it over the network in all standard deployments |
| AC:L | Low | No race conditions or special configuration required |
| PR:L | Low | Requires ability to submit code through an agent (typical end-user privilege) |
| UI:N | None | No victim interaction |
| S:C | Changed | Escapes subprocess sandbox into full host process context |
| C:H | High | Arbitrary file read, environment variable access, credential exfiltration |
| I:H | High | Arbitrary file write, arbitrary code execution on host |
| A:H | High | Can terminate processes, exhaust resources |
Affected
- Package:
praisonaiagents(PyPI) - Affected versions: all versions shipping
sandbox_mode="sandbox"(default since introduction) through 1.5.113 - Patched versions: none at time of report
- File:
praisonaiagents/tools/python_tools.py
Root Cause
Two separate blocked_attrs sets — one much weaker than the other
_validate_code_ast() (line 39, used by direct mode) blocks 30+ attributes:
# python_tools.py line 52 — direct mode (FULL list, correctly includes frame attrs)
_blocked_attrs = frozenset({
'__subclasses__', '__bases__', '__mro__', '__globals__',
'__code__', '__class__', '__dict__', '__builtins__',
'__import__', '__loader__', '__spec__', '__init_subclass__',
'__set_name__', '__reduce__', '__reduce_ex__',
'__traceback__', '__qualname__', '__module__', # ← present here
'__wrapped__', '__closure__', '__annotations__',
'gi_frame', 'gi_code', 'cr_frame', 'cr_code',
'ag_frame', 'ag_code', 'tb_frame', 'tb_next', # ← present here
'f_globals', 'f_locals', 'f_builtins', 'f_code', # ← present here
'co_consts', 'co_names',
})
The subprocess wrapper script written to disk (line 143) uses a separate,
inline blocked_attrs with only 11 entries — the frame-traversal attrs are
missing:
# python_tools.py line 143 — subprocess wrapper (SHORT list, MISSING frame attrs)
blocked_attrs = {
'__subclasses__', '__bases__', '__mro__', '__globals__',
'__code__', '__class__', '__dict__', '__builtins__',
'__import__', '__loader__', '__spec__'
# __traceback__, tb_frame, f_back, f_builtins — all absent
}
_safe_getattr does not protect direct dot-notation access
_safe_getattr replaces the getattr builtin and blocks attribute names
starting with _. However, it is only invoked when user code explicitly calls
getattr(obj, name). Direct attribute access (obj.attr in source) uses
Python's C-level tp_getattro slot and never passes through _safe_getattr.
The AST check is therefore the only barrier to direct attribute…
- Record
GHSA-qf73-2hrx-xprp- CVSS 3.1
- 9.9 Critical
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H - Weakness
- CWE-657, CWE-693
- Updated
- 2026-11-02
NVD
9.9
PraisonAI is a multi-agent teams system. Prior to 1.5.115, execute_code() in praisonaiagents.tools.python_tools defaults to sandbox_mode="sandbox", which runs user code in a subprocess wrapped with a restricted builtins dict and an AST-based blocklist. The AST blocklist embedded inside the subprocess wrapper (blocked_attrs of python_tools.py) contains only 11 attribute names — a strict subset of the 30+ names blocked in the direct-execution path. The four attributes that form a frame-traversal chain out of the sandbox are all absent from the subprocess list (traceback, tb_frame, f_back, and f_builtins). Chaining these attributes through a caught exception exposes the real Python builtins dict of the subprocess wrapper frame, from which exec can be retrieved and called under a non-blocked variable name — bypassing every remaining security layer. This vulnerability is fixed in 1.5.115.
- Record
CVE-2026-39888- CVSS 3.1
- 9.9 Critical
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H - Weakness
- CWE-657, CWE-693
- Updated
- 2026-11-02
OSVGHSA-qf73-2hrx-xprp
9.9
PraisonAI has sandbox escape via exception frame traversal in `execute_code` (subprocess mode)
Summary
execute_code() in praisonaiagents.tools.python_tools defaults to
sandbox_mode="sandbox", which runs user code in a subprocess wrapped with a
restricted __builtins__ dict and an AST-based blocklist. The AST blocklist
embedded inside the subprocess wrapper (blocked_attrs, line 143 of
python_tools.py) contains only 11 attribute names — a strict subset of the 30+
names blocked in the direct-execution path. The four attributes that form a
frame-traversal chain out of the sandbox are all absent from the subprocess list:
| Attribute | In subprocess blocked_attrs | In direct-mode _blocked_attrs |
|---|---|---|
| __traceback__ | NO | YES |
| tb_frame | NO | YES |
| f_back | NO | YES |
| f_builtins | NO | YES |
Chaining these attributes through a caught exception exposes the real Python
builtins dict of the subprocess wrapper frame, from which exec can be
retrieved and called under a non-blocked variable name — bypassing every
remaining security layer.
Tested and confirmed on praisonaiagents 1.5.113 (latest), Python 3.10.
Severity
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H — 9.9 Critical
| Vector | Value | Rationale |
|---|---|---|
| AV:N | Network | execute_code is a designated agent tool; user/LLM-supplied code reaches it over the network in all standard deployments |
| AC:L | Low | No race conditions or special configuration required |
| PR:L | Low | Requires ability to submit code through an agent (typical end-user privilege) |
| UI:N | None | No victim interaction |
| S:C | Changed | Escapes subprocess sandbox into full host process context |
| C:H | High | Arbitrary file read, environment variable access, credential exfiltration |
| I:H | High | Arbitrary file write, arbitrary code execution on host |
| A:H | High | Can terminate processes, exhaust resources |
Affected
- Package:
praisonaiagents(PyPI) - Affected versions: all versions shipping
sandbox_mode="sandbox"(default since introduction) through 1.5.113 - Patched versions: none at time of report
- File:
praisonaiagents/tools/python_tools.py
Root Cause
Two separate blocked_attrs sets — one much weaker than the other
_validate_code_ast() (line 39, used by direct mode) blocks 30+ attributes:
# python_tools.py line 52 — direct mode (FULL list, correctly includes frame attrs)
_blocked_attrs = frozenset({
'__subclasses__', '__bases__', '__mro__', '__globals__',
'__code__', '__class__', '__dict__', '__builtins__',
'__import__', '__loader__', '__spec__', '__init_subclass__',
'__set_name__', '__reduce__', '__reduce_ex__',
'__traceback__', '__qualname__', '__module__', # ← present here
'__wrapped__', '__closure__', '__annotations__',
'gi_frame', 'gi_code', 'cr_frame', 'cr_code',
'ag_frame', 'ag_code', 'tb_frame', 'tb_next', # ← present here
'f_globals', 'f_locals', 'f_builtins', 'f_code', # ← present here
'co_consts', 'co_names',
})
The subprocess wrapper script written to disk (line 143) uses a separate,
inline blocked_attrs with only 11 entries — the frame-traversal attrs are
missing:
# python_tools.py line 143 — subprocess wrapper (SHORT list, MISSING frame attrs)
blocked_attrs = {
'__subclasses__', '__bases__', '__mro__', '__globals__',
'__code__', '__class__', '__dict__', '__builtins__',
'__import__', '__loader__', '__spec__'
# __traceback__, tb_frame, f_back, f_builtins — all absent
}
_safe_getattr does not protect direct dot-notation access
_safe_getattr replaces the getattr builtin and blocks attribute names
starting with _. However, it is only invoked when user code explicitly calls
getattr(obj, name). Direct attribute access (obj.attr in source) uses
Python's C-level tp_getattro slot and never passes through _safe_getattr.
The AST check is therefore the only barrier to direct attribute…
- Record
GHSA-qf73-2hrx-xprp- CVSS 3.1
- 9.9 Critical
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H - Weakness
- CWE-657, CWE-693
- Updated
- 2026-11-02
OSVPYSEC-2026-486
9.9
PraisonAI has sandbox escape via exception frame traversal in `execute_code` (subprocess mode)
Summary
execute_code() in praisonaiagents.tools.python_tools defaults to
sandbox_mode="sandbox", which runs user code in a subprocess wrapped with a
restricted __builtins__ dict and an AST-based blocklist. The AST blocklist
embedded inside the subprocess wrapper (blocked_attrs, line 143 of
python_tools.py) contains only 11 attribute names — a strict subset of the 30+
names blocked in the direct-execution path. The four attributes that form a
frame-traversal chain out of the sandbox are all absent from the subprocess list:
| Attribute | In subprocess blocked_attrs | In direct-mode _blocked_attrs |
|---|---|---|
| __traceback__ | NO | YES |
| tb_frame | NO | YES |
| f_back | NO | YES |
| f_builtins | NO | YES |
Chaining these attributes through a caught exception exposes the real Python
builtins dict of the subprocess wrapper frame, from which exec can be
retrieved and called under a non-blocked variable name — bypassing every
remaining security layer.
Tested and confirmed on praisonaiagents 1.5.113 (latest), Python 3.10.
Severity
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H — 9.9 Critical
| Vector | Value | Rationale |
|---|---|---|
| AV:N | Network | execute_code is a designated agent tool; user/LLM-supplied code reaches it over the network in all standard deployments |
| AC:L | Low | No race conditions or special configuration required |
| PR:L | Low | Requires ability to submit code through an agent (typical end-user privilege) |
| UI:N | None | No victim interaction |
| S:C | Changed | Escapes subprocess sandbox into full host process context |
| C:H | High | Arbitrary file read, environment variable access, credential exfiltration |
| I:H | High | Arbitrary file write, arbitrary code execution on host |
| A:H | High | Can terminate processes, exhaust resources |
Affected
- Package:
praisonaiagents(PyPI) - Affected versions: all versions shipping
sandbox_mode="sandbox"(default since introduction) through 1.5.113 - Patched versions: none at time of report
- File:
praisonaiagents/tools/python_tools.py
Root Cause
Two separate blocked_attrs sets — one much weaker than the other
_validate_code_ast() (line 39, used by direct mode) blocks 30+ attributes:
# python_tools.py line 52 — direct mode (FULL list, correctly includes frame attrs)
_blocked_attrs = frozenset({
'__subclasses__', '__bases__', '__mro__', '__globals__',
'__code__', '__class__', '__dict__', '__builtins__',
'__import__', '__loader__', '__spec__', '__init_subclass__',
'__set_name__', '__reduce__', '__reduce_ex__',
'__traceback__', '__qualname__', '__module__', # ← present here
'__wrapped__', '__closure__', '__annotations__',
'gi_frame', 'gi_code', 'cr_frame', 'cr_code',
'ag_frame', 'ag_code', 'tb_frame', 'tb_next', # ← present here
'f_globals', 'f_locals', 'f_builtins', 'f_code', # ← present here
'co_consts', 'co_names',
})
The subprocess wrapper script written to disk (line 143) uses a separate,
inline blocked_attrs with only 11 entries — the frame-traversal attrs are
missing:
# python_tools.py line 143 — subprocess wrapper (SHORT list, MISSING frame attrs)
blocked_attrs = {
'__subclasses__', '__bases__', '__mro__', '__globals__',
'__code__', '__class__', '__dict__', '__builtins__',
'__import__', '__loader__', '__spec__'
# __traceback__, tb_frame, f_back, f_builtins — all absent
}
_safe_getattr does not protect direct dot-notation access
_safe_getattr replaces the getattr builtin and blocks attribute names
starting with _. However, it is only invoked when user code explicitly calls
getattr(obj, name). Direct attribute access (obj.attr in source) uses
Python's C-level tp_getattro slot and never passes through _safe_getattr.
The AST check is therefore the only barrier to direct attribute…
- Record
PYSEC-2026-486- CVSS 3.1
- 9.9 Critical
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H - Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
praisonaiagentsPyPI |
through 1.5.114 | 1.5.115 |
praisonaiagentsPyPI |
before 1.5.115, plus listed versions 0.0.10.0.100.0.1000.0.1010.0.102 Show 45 moreShow less0.0.1030.0.1040.0.1050.0.1060.0.1070.0.1080.0.1090.0.110.0.1100.0.1110.0.1120.0.1130.0.1140.0.1150.0.1160.0.1170.0.1180.0.1190.0.120.0.1200.0.1210.0.1220.0.1230.0.1240.0.1250.0.1260.0.1270.0.1280.0.1290.0.130.0.1300.0.1310.0.1320.0.1330.0.1340.0.1350.0.1360.0.1370.0.1380.0.1390.0.140.0.1400.0.1410.0.1420.0.143 |
1.5.115 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-39888 is a critical vulnerability (CVSS 10.0) in Mervinpraison Praisonaiagents, published April 8, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.50%, the 43rd percentile. Fixed in praisonaiagents 1.5.115.
References
Left out here: 444 versions and the rest of 3 source descriptions. The JSON record has them.