Apr 7, 2026·5 min read·1 visit
Lupa <= 2.6 fails to enforce attribute_filter on Python built-ins like getattr(). Attackers can exploit this to access __class__ and __mro__, resulting in a sandbox escape and RCE. Update to Lupa 2.7+ or disable register_builtins.
CVE-2026-34444 is a critical sandbox escape vulnerability in the Lupa Python library, allowing remote code execution. The flaw arises from an incomplete attribute_filter implementation that fails to hook Python built-in functions like getattr and setattr, enabling attackers to bypass restrictions and access sensitive internal attributes.
Lupa integrates Lua and LuaJIT2 runtimes into CPython environments. It provides a mechanism to execute Lua code within a sandboxed environment while allowing controlled interaction with Python objects. The attribute_filter callback enforces access controls on Python attributes accessed from the Lua runtime.
CVE-2026-34444 represents an access control bypass vulnerability within this interaction layer. The attribute_filter successfully prevents direct attribute access from Lua syntax. However, the enforcement mechanism is incomplete and fails to govern access facilitated through Python built-in functions.
Attackers leverage this bypass to traverse the Python object hierarchy. By accessing restricted attributes such as __class__ and __mro__, adversaries achieve a complete sandbox escape resulting in arbitrary code execution within the host Python process.
The attribute_filter function serves as a security boundary between the Lua runtime and the host Python environment. When Lua code attempts to access a Python object attribute using standard syntax, Lupa intercepts the request. The wrapper object evaluates the requested attribute against the defined filter before granting access.
The vulnerability exists because this interception logic only hooks direct attribute access originating from the Lua interpreter. Python built-in functions, specifically getattr() and setattr(), operate directly on the underlying CPython objects. When these built-ins are exposed to the Lua environment, they execute without passing through Lupa's attribute filter wrapper.
If a developer initializes the LuaRuntime with Python built-ins enabled, an attacker can invoke getattr() from within the Lua script. Because getattr is a native CPython function, it retrieves the requested attribute natively, bypassing the custom wrapper and the associated security filter entirely.
During standard operation, a Lua script accessing an object attribute interacts with a proxy layer. Lupa wraps Python objects passed into the Lua environment. The wrapper's attribute accessor invokes the developer-defined attribute_filter callback. If the callback denies access, the wrapper raises an AttributeError.
When python.builtins is registered, the Lua environment gains a direct reference to the native CPython getattr function. The Lua script executes getattr(target_object, "__class__"). This execution path circumvents the Lupa proxy layer entirely.
The patch for CVE-2026-34444 introduces enforcement of the attribute_filter across all access methods. The structural fix requires Lupa to intercept calls to built-in functions or replace the exposed built-ins with wrapped equivalents that respect the established runtime filters.
Exploitation requires the Lua environment to possess access to Python built-in functions. This typically occurs when register_builtins=True is passed to the LuaRuntime constructor or when specific built-ins are manually injected into the global namespace. Once this prerequisite is met, the attacker executes a standard Python sandbox escape sequence.
The attacker retrieves the getattr function and applies it to any accessible Python object. This grants access to the object's __class__ attribute. The attacker then traverses the Method Resolution Order (MRO) via __mro__ to locate the base object class.
From the base object class, the attacker invokes __subclasses__() to enumerate all classes currently loaded in the CPython interpreter. The exploit payload iterates through this list to identify classes referencing sensitive modules, such as os._wrap_close. By accessing the __globals__ dictionary of the initialization method, the attacker retrieves the system function and executes arbitrary operating system commands.
local py = python.builtins
local getattr = py.getattr
local cls = getattr(user, "__class__")
local _, obj_cls = getattr(cls, "__mro__")
local subs = getattr(obj_cls, "__subclasses__")()Successful exploitation yields arbitrary code execution within the context of the host Python process. The attacker inherits the privileges of the application executing the Lupa runtime. This enables unauthorized read and write access to the underlying filesystem, interaction with network resources, and potential lateral movement within the infrastructure.
The vulnerability carries a CVSS 4.0 base score of 7.9 (High). The attack vector is Network (AV:N), assuming the application accepts user-supplied Lua scripts remotely. Attack complexity is Low (AC:L), and no special privileges (PR:N) or user interaction (UI:N) are required.
The EPSS score stands at 0.00068 (20.77th percentile), indicating a relatively low probability of widespread, indiscriminate exploitation. However, targeted attacks against systems executing untrusted Lua scripts remain highly viable. The vulnerability is not currently listed in the CISA Known Exploited Vulnerabilities (KEV) catalog.
System administrators and developers must upgrade the lupa package to version 2.7 or later. The updated release correctly applies the attribute_filter to object accesses mediated by Python built-in functions. This structural change eliminates the bypass vector and restores the integrity of the sandbox.
Organizations unable to deploy the update immediately must implement configuration changes to the LuaRuntime initialization. Administrators must ensure that register_builtins is set to False. This is the default behavior, but explicit verification is required for all instantiations.
# Secure configuration
lua = LuaRuntime(register_builtins=False, attribute_filter=protected_attribute_filter)If the application architecture necessitates exposing specific Python functions to the Lua runtime, developers must adopt an explicit allowlist approach. Rather than registering the entire python.builtins module, only specific, non-sensitive functions should be injected into the Lua global namespace.
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:H/SI:H/SA:H| Product | Affected Versions | Fixed Version |
|---|---|---|
lupa scoder | <= 2.6 | 2.7 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-284, CWE-639 |
| Attack Vector | Network |
| CVSS 4.0 | 7.9 (High) |
| EPSS Score | 0.00068 (20.77%) |
| Impact | Remote Code Execution (RCE) |
| Exploit Status | PoC Available |
| CISA KEV | Not Listed |
Improper Access Control and Authorization Bypass Through User-Controlled Key