Aug 29, 2026·6 min read·6 visits
Incomplete validation of positional-only arguments in RestrictedPython allowed attackers to shadow security hooks and escape the sandbox.
A critical security flaw was identified in RestrictedPython prior to version 8.3 where positional-only arguments introduced in Python 3.8 were not properly validated. This allowed an attacker executing code within the sandbox to shadow critical security guards like `_write_` and `_getattr_`, leading to a complete sandbox escape and arbitrary code execution on the underlying server.
RestrictedPython is a specialized tool designed to define a safe subset of the Python language. It is commonly integrated into frameworks such as Zope and Plone to allow the execution of untrusted, user-supplied code in a restricted execution environment. To achieve this, RestrictedPython parses source code into an Abstract Syntax Tree (AST) and rewrites potentially hazardous operations into safe, validation-wrapped calls.
This rewritten AST relies on security guard hooks such as _getattr_, _getitem_, and _write_ to validate attribute access, subscripts, and mutation operations at runtime. These hooks are dynamically injected into the restricted program's global execution namespace. The security model relies entirely on preventing the sandboxed code from modifying or shadowing these protected identifiers, which are reserved and prefixed with leading underscores.
Prior to version 8.3, a structural omission existed in RestrictedPython's AST validation parser. When defining function parameter names, the validation engine failed to inspect positional-only arguments introduced in Python 3.8. This failure allows an attacker to bypass name-checking restrictions, leading to complete sandbox evasion and potential arbitrary code execution.
The root cause of this vulnerability lies in the Abstract Syntax Tree (AST) translation phase, specifically within the check_function_argument_names method inside src/RestrictedPython/transformer.py. This method is responsible for validating all parameter names in function and lambda definitions. Its purpose is to ensure that no parameter shadows the injected security hooks by starting with an underscore, such as _write_ or _getattr_.
In Python 3.8, PEP 570 introduced positional-only parameters, which are separated from other arguments by a forward slash (/). To support this syntax, the Python AST was updated to store these parameters in a distinct list field named posonlyargs under the function arguments node. However, the AST transformer in RestrictedPython was not updated to iterate through this newly introduced list.
Consequently, the validation loop inside check_function_argument_names checked standard arguments (args), keyword-only arguments (kwonlyargs), variable arguments (vararg), and keyword arguments (kwarg), but entirely omitted posonlyargs. This omission allowed positional-only arguments named after security hooks to pass compilation without throwing an error.
A review of the vulnerable implementation in src/RestrictedPython/transformer.py highlights the missing check for the posonlyargs collection.
# Vulnerable implementation in RestrictedPython/transformer.py
def check_function_argument_names(self, node):
# The posonlyargs collection is completely ignored here
for arg in node.args.args:
self.check_name(node, arg.arg)
for arg in node.args.kwonlyargs:
self.check_name(node, arg.arg)
if node.args.vararg is not None:
self.check_name(node, node.args.vararg.arg)
if node.args.kwarg is not None:
self.check_name(node, node.args.kwarg.arg)The patch applied in commit 3737596ec9f28c34a073cc845bd2f4c0a80cb671 addresses this gap by prepending a loop that checks node.args.posonlyargs before verifying the remaining argument types.
# Patched implementation in RestrictedPython/transformer.py
def check_function_argument_names(self, node):
# Added check to ensure positional-only parameters are validated
for arg in node.args.posonlyargs:
self.check_name(node, arg.arg)
for arg in node.args.args:
self.check_name(node, arg.arg)
for arg in node.args.kwonlyargs:
self.check_name(node, arg.arg)
if node.args.vararg is not None:
self.check_name(node, node.args.vararg.arg)
if node.args.kwarg is not None:
self.check_name(node, node.args.kwarg.arg)This simple addition closes the validation gap. Any attempt to use restricted identifiers in positional-only parameters will trigger a compilation error, maintaining the integrity of the sandbox's execution environment.
Exploitation of CVE-2026-55830 requires the capability to compile and execute custom Python scripts within the restricted environment. Because the compiler fails to check positional-only arguments, an attacker can define a function or lambda where a positional-only argument is named exactly after a protected global guard, such as _write_.
# Shadowing the global write guard hook
def escape_sandbox(_write_=lambda obj: obj, /):
# Inside this scope, attribute write operations use the local _write_
passWhen Python compiles this function, it generates bytecode that resolves _write_ using the local scope rather than the global environment. At runtime, the local parameter _write_ is initialized with the attacker-controlled default lambda value. Any attribute write operation executed within the scope of this function is redirected through this lambda, which bypasses the validation and returns the raw target object.
An attacker can use this bypassed validation to access and modify the execution environment's internal namespace. By overriding the global dictionary's _getattr_ and _write_ hooks, the attacker disables the security boundary completely. This allows the script to import standard modules such as os or subprocess and execute arbitrary system commands on the host machine.
The security impact of CVE-2026-55830 is classified as High, with a CVSS v3.1 base score of 8.3. The vulnerability represents a complete breakdown of the isolation boundary provided by RestrictedPython, allowing sandboxed code to escape and achieve execution in the context of the parent application process.
An attacker who successfully escapes the sandbox can read and write arbitrary files, compromise system configurations, and access database credentials or environment variables stored on the host. If the parent application is running with elevated privileges, the entire hosting environment can be compromised.
While the vulnerability requires high privileges to submit and run scripts, the exploitation complexity is low because the bypass works reliably without relying on race conditions or system state. The scope change metric is set to 'Changed' because the impact extends from the restricted interpreter to the host operating system.
The recommended remediation for this vulnerability is to upgrade RestrictedPython to version 8.3 or higher. If immediate upgrading is not possible, defensive mitigations must be implemented to inspect user-provided scripts prior to passing them to the RestrictedPython compiler.
Organizations can deploy an AST pre-filter utility that parses the incoming source code and rejects any structure containing positional-only arguments that begin with an underscore. This can be integrated into the application's intake validation pipelines.
import ast
def validate_ast_safety(source: str) -> bool:
try:
tree = ast.parse(source)
except SyntaxError:
return False
for node in ast.walk(tree):
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.Lambda)):
for arg in node.args.posonlyargs:
if arg.arg.startswith('_'):
return False
return TrueAdditionally, applications should employ strong process isolation. Running the python process within a containerized environment with minimal privileges and strict network access controls can limit the post-exploitation damage of a sandbox escape.
CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:H/I:H/A:L| Attribute | Detail |
|---|---|
| CWE ID | CWE-184 |
| Attack Vector | Network |
| CVSS v3.1 | 8.3 (High) |
| EPSS Score | 0.00401 (Percentile: 33.04%) |
| Exploit Status | Proof of Concept (PoC) |
| CISA KEV Status | Not Listed |
| Impact | Arbitrary Code Execution (ACE) via Sandbox Escape |
An input validation and encoding desynchronization vulnerability exists in MariaDB Connector/R2DBC versions prior to 1.4.1. The driver assumes all communication utilizes the UTF-8 character set, but fails to account for server-driven mid-session changes to the character_set_client variable. When a change to a multi-byte character set such as GBK or Big5 is induced, the server interprets client-escaped single quotes as part of a multi-byte character. This state desynchronization bypasses standard escaping mechanisms and allows remote unauthenticated attackers to execute arbitrary SQL commands.
A security vulnerability in the MariaDB Connector/R2DBC client driver allows credential theft during the database authentication phase. The client driver does not gate clear-text password authentication plugins on transport encryption, making it possible for on-path attackers or hostile database servers to intercept passwords.
CVE-2026-55855 is a client-side SQL injection vulnerability in the MariaDB Connector/Node.js library that occurs when using legacy multi-byte character sets. The flaw arises from naive, byte-wise client-side parameter escaping. Attackers can leverage specific multi-byte lead bytes to absorb backslash escape characters on the server side, allowing them to terminate string literals and execute arbitrary SQL commands.
An improper authentication vulnerability (CWE-287) in Portainer Community Edition (CE) allows unauthenticated remote attackers to achieve full administrative takeover. During the initial five-minute uninitialized setup window, sensitive endpoints responsible for creating the initial administrator user and restoring database state are publicly accessible without authentication. Attackers can exploit this to create administrative credentials or overwrite the system state with a malicious database configuration.
CVE-2026-55678 defines a critical security vulnerability in the Enterprise clustering implementation of Arc, an open-source SQL-native time-series database. When clustering is enabled but a shared secret is not defined, the cluster coordinator fails to enforce authentication on cluster join requests and node status updates. Remote, unauthenticated attackers can exploit this behavior to register a rogue node, hijack telemetry routing, and harvest sensitive client authentication headers.
A critical security vulnerability exists in plone.app.event, the event content type package for the Plone CMS. Prior to versions 5.2.4 and 6.0.1, the iCalendar import component lacked proper file size controls, URL scheme validation, and network isolation filters. Authenticated editors could exploit these deficiencies to cause denial of service via memory exhaustion, read local files, perform server-side request forgery, and inject stored cross-site scripting vectors.