Python Pickle 反序列化漏洞学习与防护
一、漏洞背景
在 Python 中,pickle 是一个常见的序列化模块,可以把 Python 对象转换成字节流,也可以把字节流还原成对象。
序列化常用于:
- 本地缓存对象
- 保存程序运行状态
- 进程间传递对象
- 简单的数据持久化
但如果程序对不可信数据执行 pickle.loads() 或 pickle.load(),就可能触发反序列化漏洞。攻击者可以构造特殊对象,让程序在反序列化过程中执行预设函数,从而造成命令执行风险。
本文只在本地靶场和授权环境中演示,用于理解漏洞原理和防护思路。
二、Pickle 基础用法
1. 序列化
1 | import pickle |
pickle.dumps() 会将对象转换成字节流。
2. 反序列化
1 | import pickle |
pickle.loads() 会把字节流恢复成原来的 Python 对象。
如果数据来源可信,这个过程通常没有问题。但如果 data_bytes 来自用户输入、Cookie、请求参数、上传文件或外部接口,就需要非常谨慎。
三、漏洞原理
Pickle 反序列化风险的核心在于:对象可以通过特殊方法控制反序列化时的还原逻辑。
其中最常见的是 __reduce__() 方法。
当对象被 pickle 反序列化时,__reduce__() 可以返回一个可调用对象和参数。反序列化时 Python 会调用该对象,并传入参数。
简单理解:
1 | 攻击者构造对象 -> 序列化成字节流 -> 服务端 loads -> 自动调用指定函数 |
这也是为什么不应该对不可信数据使用 Pickle。
四、本地演示
下面用一个无害命令演示触发过程。
1 | import pickle |
运行结果会执行:
1 | echo pickle_demo |
这个例子说明:反序列化过程并不只是“恢复数据”,它可能执行函数调用。
五、Base64 传输场景
在 Web 题目或接口中,Pickle 数据经常会被 Base64 编码后传输。
生成 payload:
1 | import base64 |
服务端如果这样处理用户输入,就存在风险:
1 | import base64 |
问题不在 Base64,而在于服务端对用户可控内容执行了 pickle.loads()。
六、常见危险代码特征
审计 Python Web 项目时,可以重点搜索以下代码:
1 | pickle.load |
高风险组合:
1 | pickle.loads(request.data) |
如果反序列化对象来自用户输入或上传文件,就需要重点关注。
七、CTF 中的常见思路
CTF 中常见的 Pickle 题会把序列化数据放在:
- Cookie
- Flask session
- POST 参数
- Base64 字符串
- 上传文件
解题思路一般是:
1 | 找到反序列化入口 -> 判断数据编码方式 -> 构造 __reduce__ -> 编码 payload -> 触发 loads |
如果题目过滤了关键字,可以尝试:
- 使用
__import__动态导入模块 - 换用不同可调用对象
- 分析过滤逻辑是否只过滤字符串明文
- 结合源码寻找可利用函数
但在真实业务中,不应该依赖过滤关键字来防护 Pickle 反序列化。
八、防护建议
1. 不反序列化不可信数据
最重要的原则:
1 | 不要对用户可控数据使用 pickle.loads() |
如果只是传递普通数据,优先使用 JSON:
1 | import json |
JSON 只表示数据结构,不会像 Pickle 一样恢复任意 Python 对象。
2. 做数据签名
如果业务必须存储序列化数据,应至少做完整性校验,例如 HMAC 签名:
1 | import hmac |
服务端校验签名通过后再处理数据,避免用户篡改内容。
不过签名只能防篡改,不能改变 Pickle 本身的危险特性。
3. 限制反序列化类型
可以自定义 Unpickler 限制允许加载的类,但实现复杂,容易漏掉风险。真实业务中更推荐从设计上避免使用 Pickle 处理外部输入。
4. 最小权限运行服务
即使发生漏洞,也应降低影响:
- Web 服务使用低权限用户运行。
- 容器或沙箱隔离。
- 限制文件读写权限。
- 限制外连能力。
- 记录异常请求日志。
九、排查思路
如果怀疑系统存在 Pickle 反序列化风险,可以按以下流程排查:
1 | 搜索 pickle.loads -> 确认数据来源 -> 判断是否用户可控 -> 检查是否签名校验 -> 复现验证 -> 替换为安全格式 |
日志上可以关注:
- 参数中出现长 Base64 字符串。
- Cookie 体积异常变大。
- 服务端出现系统命令执行痕迹。
- Web 进程产生异常子进程。
- 反序列化报错堆栈。
十、总结
Pickle 反序列化漏洞的本质是:程序把不可信输入当成可信对象恢复,并在恢复过程中执行了对象指定的逻辑。
学习这类漏洞时,重点不是背 payload,而是理解:
pickle.loads()为什么危险。__reduce__()如何影响反序列化流程。- Web 场景中用户输入如何进入反序列化入口。
- 真实业务中应如何替换和加固。
在生产环境中,最稳妥的做法是:外部输入使用 JSON 等安全数据格式,避免使用 Pickle 处理用户可控内容。