Deserialization Vulnerability Patterns (CWE-502)
当审计 Python 代码中涉及反序列化操作、模型加载、数据持久化读取时加载此 Skill。
Detection Strategy
反序列化漏洞的核心模式是:不可信数据 进入 反序列化函数,且中间缺乏有效的安全屏障。Python 的反序列化漏洞尤其危险,因为 pickle、shelve、torch.load 等机制原生支持通过 __reduce__ 方法执行任意代码。
Sources(不可信数据来源):
- HTTP 请求体(REST API、gRPC、Flight RPC)
- 用户上传的模型文件(.pt, .pkl, .bin, .keras, .ckpt)
- ZeroMQ / TCP Socket 接收的数据
- 用户控制的 JSON 数据中的
__type__/__class__字段 - 自定义序列化框架的 fallback 路径
- 文件系统中可被路径遍历访问的 session 文件
- 数据库或缓存中的序列化对象(shelve, dbm)
- 跨节点分布式通信数据(torch.distributed, Ray, Dask)
Sinks(反序列化函数):
pickle.loads()/pickle.load()— 直接反序列化zmq.Socket.recv_pyobj()— 内部调用pickle.loads()torch.load()— 不带weights_only=True时使用 pickleshelve.open()— 内部使用 pickle 进行序列化/反序列化json.loads()配合自定义object_hook+importlib.import_module()— 动态类实例化cloudpickle.loads()/dill.loads()— pickle 变体yaml.load()— 不使用safe_load时可执行任意 Python 对象joblib.load()— 内部使用 picklemarshal.loads()— 可执行字节码Unpickler.load()— 自定义 Unpickler 若未限制find_class仍然危险
Sanitization(安全屏障):
- 使用
yaml.safe_load()替代yaml.load() - 使用
torch.load(weights_only=True)限制只加载张量 - 使用
json.loads()配合白名单object_hook(仅允许已注册类型) - 自定义
Unpickler并重写find_class()限制允许的类 - 对序列化数据进行签名验证(HMAC)后再反序列化
- 使用
safetensors格式替代 pickle 格式的模型文件 - 使用
pickle.loads()前通过fickling或picklescan扫描恶意 opcode - 在 HTTP API 层面拒绝
application/vnd.bentoml+pickle等危险 Content-Type - 使用
struct.pack/unpack、json、protobuf等安全序列化替代 pickle - 绑定服务到
127.0.0.1而非0.0.0.0,配合认证机制
检测路径:
- 搜索反序列化函数调用:Grep
pickle.loads,pickle.load,recv_pyobj,torch.load,shelve.open,yaml.load,joblib.load,marshal.loads,cloudpickle,dill.loads - 搜索隐式反序列化:Grep
importlib.import_module+object_hook,__type__,__class__,getattr组合模式;检查from_payload,deserialize等方法中是否有 pickle - 检查数据来源是否可被外部控制:从 sink 参数向上回溯,确认数据是否来自网络请求、用户上传文件、外部 Socket
- 验证是否使用了安全的反序列化方式:检查是否有白名单、签名验证、
weights_only=True、safe_load等防护 - 若使用不安全方式且数据来源不可信 -> 标记为候选漏洞
Detection Checklist
- 搜索
pickle.loads/pickle.load调用,检查数据来源是否可被外部控制 - 搜索
recv_pyobj()调用(ZeroMQ),这是隐式pickle.loads - 搜索
torch.load调用,检查是否缺少weights_only=True参数 - 搜索
shelve.open调用,检查存储路径是否可被外部控制 - 搜索
json.loads+ 自定义object_hook,检查是否有importlib.import_module或动态getattr调用 - 搜索
yaml.load调用,检查是否使用了Loader=SafeLoader或yaml.safe_load - 检查 HTTP API 中是否接受 pickle 相关的 Content-Type(如
application/vnd.bentoml+pickle) - 检查序列化框架(如 pyfory/pyfury)是否存在 pickle fallback 路径
- 检查模型文件扫描逻辑是否正确处理了
scan_err(扫描失败时是否放行) - 检查分布式通信中的序列化方式(ZeroMQ, gRPC, TCPStore),是否绑定到
0.0.0.0 - 检查 session 存储路径是否有路径遍历防护(
safe_joinvsos.path.join) - 搜索
joblib.load调用,检查加载路径/数据是否来自不可信来源(joblib 内部使用 pickle) - 搜索
marshal.loads调用,检查是否反序列化不可信来源的字节码
False Positive Exclusion Guide
以下情况通常不是漏洞:
pickle.loads的数据来源是本地可信文件且路径不可被外部控制torch.load使用了weights_only=True参数yaml.load使用了Loader=yaml.SafeLoader或调用的是yaml.safe_load- 自定义
object_hook中有严格的类型白名单(只允许已注册类型) - 反序列化前有 HMAC 签名验证
- 服务仅监听
127.0.0.1且有认证机制保护 - 使用了
fickling/picklescan扫描且正确处理了扫描错误 pickle.loads的数据来自同进程内部的序列化(无网络/文件边界)
以下情况需要额外关注:
picklescan扫描后只检查infected_files != 0但未检查scan_err(扫描本身失败时会放行恶意文件)- 自定义序列化框架声称"安全"但存在 pickle fallback 路径
torch.load的weights_only参数默认值在不同版本可能不同from_config/from_payload等反序列化入口可能隐藏在深层调用链中
Real-World Cases
详见 references/cases.md