利用OpCode绕过Python沙箱
字数 771 2025-08-25 22:58:20

Python沙箱逃逸:利用OpCode绕过限制

1. OpCode基础概念

OpCode(操作码)是Python源代码编译后的结果,Python虚拟机无法直接执行人类可读的源代码,而是需要先编译为OpCode序列。

1.1 查看函数OpCode

def a():
    if 1 == 2:
        print("flag{****}")

print("Opcode of a():", a.__code__.co_code.encode('hex'))

输出结果:

Opcode of a(): 6401006402006b020072140064030047486e000064000053

1.2 使用dis模块解析OpCode

import dis
dis.dis('6401006402006b020072140064030047486e000064000053'.decode('hex'))

解析结果:

0 LOAD_CONST               1 (1)
3 LOAD_CONST               2 (2)
6 COMPARE_OP              2 (==)
9 POP_JUMP_IF_FALSE       20
12 LOAD_CONST              3 (3)
15 LOAD_BUILD_CLASS
16 YIELD_FROM
17 JUMP_FORWARD            0 (to 20)
>> 20 LOAD_CONST              0 (0)
23 RETURN_VALUE

2. 常见字节码指令

变量 指令名 操作
LOAD_GLOBAL 读取全局变量
STORE_GLOBAL 给全局变量赋值
LOAD_FAST 读取局部变量
STORE_FAST 给局部变量赋值
LOAD_CONST 读取常量
POP_JUMP_IF_FALSE 当条件为假的时候跳转
JUMP_FORWARD 直接跳转
COMPARE_OP 比较操作

3. 利用OpCode改变程序逻辑

3.1 方法一:直接读取常量

def a():
    if 1 == 2:
        print("flag{****}")

print(a.__code__.co_consts)  # 输出所有常量

3.2 方法二:修改函数代码对象

CodeType构造函数:

def __init__(self, argcount, nlocals, stacksize, flags, code, consts, names, varnames, filename, name, firstlineno, lnotab, freevars=None, cellvars=None):

获取函数代码对象属性:

def a():
    if 1 == 2:
        print("flag{****}")

for name in dir(a.__code__):
    print(name, getattr(a.__code__, name))

构造新代码对象:

def a():
    if 1 == 2:
        print("flag{****}")

# 构造目标代码的OpCode
newcode = type(a.__code__)
code = "6401006402006b030072140064030047486e000064000053".decode('hex')
code = newcode(0, 0, 2, 67, code, (None, 1, 2, 'flag{****}'), (), (), "xxx", "a", 1, "")
a.__code__ = code
a()  # 输出flag

4. 闭包环境中的OpCode修改

def target(flag):
    def printflag():
        if flag == "":
            print flag
        return
    printflag

flag = target("flag{")
del target

构造替代函数:

def target(flag):
    def printflag():
        if flag != "":
            print flag
        return
    printflag

a = target("xxx")
import types
code = a.__code__.co_code.encode('hex')
print code

EXP:

newcode = type(flag.__code__)
code = "8800006401006b030072140088000047486e000064000053".decode('hex')
code = newcode(0, 0, 2, 19, code, (None, ''), (), (), "example2.py", "printflag", 2, "", ('flag',), ())
flag.__code__ = code
flag()  # 输出flag

5. 防御措施

  1. 限制对__code__属性的访问
  2. 禁止使用types.CodeTypetype(func.__code__)
  3. 监控和限制字节码操作
  4. 使用更严格的沙箱环境如PyPy沙箱

6. 总结

通过修改Python函数的OpCode,攻击者可以绕过沙箱限制,改变程序执行逻辑。这种技术的关键在于理解Python字节码的结构和操作方式,以及如何通过修改代码对象来改变函数行为。防御此类攻击需要从多个层面进行防护,包括限制敏感属性访问和监控代码修改行为。

Python沙箱逃逸:利用OpCode绕过限制 1. OpCode基础概念 OpCode(操作码)是Python源代码编译后的结果,Python虚拟机无法直接执行人类可读的源代码,而是需要先编译为OpCode序列。 1.1 查看函数OpCode 输出结果: 1.2 使用dis模块解析OpCode 解析结果: 2. 常见字节码指令 | 变量 | 指令名 | 操作 | |------|--------|------| | LOAD_ GLOBAL | 读取全局变量 | | STORE_ GLOBAL | 给全局变量赋值 | | LOAD_ FAST | 读取局部变量 | | STORE_ FAST | 给局部变量赋值 | | LOAD_ CONST | 读取常量 | | POP_ JUMP_ IF_ FALSE | 当条件为假的时候跳转 | | JUMP_ FORWARD | 直接跳转 | | COMPARE_ OP | 比较操作 | 3. 利用OpCode改变程序逻辑 3.1 方法一:直接读取常量 3.2 方法二:修改函数代码对象 CodeType构造函数: 获取函数代码对象属性: 构造新代码对象: 4. 闭包环境中的OpCode修改 构造替代函数: EXP: 5. 防御措施 限制对 __code__ 属性的访问 禁止使用 types.CodeType 或 type(func.__code__) 监控和限制字节码操作 使用更严格的沙箱环境如PyPy沙箱 6. 总结 通过修改Python函数的OpCode,攻击者可以绕过沙箱限制,改变程序执行逻辑。这种技术的关键在于理解Python字节码的结构和操作方式,以及如何通过修改代码对象来改变函数行为。防御此类攻击需要从多个层面进行防护,包括限制敏感属性访问和监控代码修改行为。