Critical

Python Sandbox Escape via Dynamically-Constructed Dunder Attribute Chain

Some AI-agent code sandboxes try to block dangerous Python features by scanning submitted code text for forbidden keywords like __class__ or __subclasses__. This rule catches attackers who defeat that scan by building those keywords piece by piece at runtime (using character codes or string concatenation) so the forbidden text never appears literally in what the sandbox inspects.

How the attack works

The attacker submits code to a sandboxed code-execution tool that never contains a literal blocked word such as __mro__ or __subclasses__. Instead the code assembles that word at runtime, either by converting character codes back to characters or by concatenating harmless string fragments containing underscores. The assembled string is then handed to a Python mechanism that resolves attributes by name -- getattr(), str.format()/format_map(), or a walk through __mro__/__subclasses__ -- which follows it straight to a real Python object outside the sandbox's intended boundary. From there the attacker can reach classes and functions the sandbox meant to hide, including ones that can run arbitrary system commands.

Netzilo detection

Netzilo reports this behaviour when it is observed.

Signature ID
8dc89e87-4a55-4325-ae33-1bc5f0984832
Severity
Critical

Why it matters

An attacker who reaches the code-execution tool can escape the sandbox entirely and run arbitrary code with the privileges of the host process, defeating the isolation the sandbox was meant to provide.

What you can do

  • Do not rely on scanning source text for blocked keywords as a sandbox defense; it can always be defeated by runtime string construction.
  • Restrict or remove access to getattr(), str.format()/format_map(), and any attribute-resolution primitives inside code-execution tools rather than trying to blocklist their arguments.
  • Run untrusted code in a real process- or OS-level sandbox (containers, seccomp, restricted interpreters) instead of a Python-level attribute blocklist.
  • Review any code-execution tool for use of chr()/ord() combined with getattr() or format(), and treat that combination as suspicious regardless of whether recognizable dunder names appear in the source.

Known benign look-alikes

  • Legitimate use of chr()/ord() in string-processing or Caesar-cipher/obfuscation-teaching code that never feeds the result into getattr(), format(), or an attribute-resolution primitive
  • Test code exercising str.format() with ordinary positional/keyword field access (e.g. '{0}', '{name}') that does not reference a dotted attribute path
  • Security-research or CTF write-ups that quote this exact bypass technique as prose/documentation rather than a submitted tool argument

References

Related threats