反序列化漏洞:Java、PHP、Python 三条线各讲透

反序列化漏洞有个很拧巴的特点:业务侧只觉得自己在"存对象、传对象、缓存对象",安全侧看到的却是------不可信数据一旦变成对象,就可能顺着构造函数、魔术方法、魔术接口,一路跑到命令执行 。于是报告标题经常直接写 RCE,开发一脸懵:"我就是 readObject / unserialize / pickle.loads 了一下啊?"

是的,问题就出在这一下。


一、先讲所有语言共用的那层道理

1. 序列化是什么

把内存里的对象变成可传输、可存储的字节或文本;反过来叫反序列化。

用途正当:缓存、RPC、Session、消息队列、SSO Token、游戏存档、任务任务体......

2. 危险协议 vs 数据协议

若格式的设计目标是"恢复出完整对象并执行类型相关逻辑",它就更危险。

若格式只是数据结构(JSON/PB 等)再手动映射到对象,通常安全得多------前提是你不要在映射后又搞一套危险动态调用。

3. 利用链(Gadget Chain)是什么

攻击者往往不能直接"命令字符串",但能控制反序列化出来的对象图。

若 classpath/环境里存在一些"看似无害、组合起来危险"的类,它们的反序列化回调(Java readObject 等)、魔术方法(PHP __wakeup/__destruct)、还原逻辑(Python __reduce__)可能拼成一条调用链,最终摸到 Runtime.execsystem、文件写入等。

所以反序列化漏洞常常是:

入口(反序列化用户数据) + 材料(可用 gadget 类) + 结果(RCE/读文件/JNDI...)

修入口最重要;减材料(升级、移出危险依赖)是纵深;监控结果是兜底。

4. 共同防御原则

  1. 不要反序列化不可信数据
  2. 必须传数据时,用 JSON 等纯数据格式 + 校验;
  3. 若不得不保留原生反序列化:签名/加密、强类型白名单、网络隔离;
  4. 升级组件,打破已知利用链;
  5. 运行时最小权限。

下面三条线,都是这五条的方言版。


二、Java 线:历史最久、事故最响、链最工业

1. 常见入口

  • ObjectInputStream.readObject() 及同类;
  • 会话持久化、RMI、JMX、自定义 RPC;
  • 一些中间件/协议自带的 Java 序列化;
  • 假"加密"的 Cookie/参数里塞序列化二进制(Base64 一眼能认)。

特征:数据常以 ac ed 00 05(Java 序列化魔数)开头,或 Base64 后以 rO0 一类开头------审计与检测常靠这个。

2. 为什么特别容易打到 RCE

Java 生态依赖树深,历史上 Commons Collections、其他流行库等与调用链组合的问题被广泛研究;安全圈有成熟的研究工具生态(授权测试中常被提及)。

对防守方这意味着:你以为只反序列化了自己的业务类,classpath 里却躺着别人的弹药。

3. 利用条件(理解用)

  • 存在反序列化入口且数据用户可控;
  • 目标 classpath 有可用链;
  • JDK/组件版本与安全过滤器(JEP290 等)是否挡住。

不是"有序列化就一定挂",但"有不可信 readObject"应默认高优先级审计。

4. 防御讲透

(1)能不用就不用

内部缓存优先 JSON/Hessian 安全模式/PB 等,并明确禁止 Java 原生序列化进公网接口。

(2)JEP 290 反序列化过滤(JDK 9+,低版本有backport思路)

配置允许的类白名单,拒绝其余。这是官方级缓解,值得做进统一工厂。

(3)看起来像白名单的库

历史上有过"黑名单过滤"被绕过的故事;白名单优于黑名单。自己维护过滤器要跟版本。

(4)升级与下线危险依赖

就算暂时关不掉入口,也要评估已知相关组件版本,减少可用链。依赖扫描进 SLA。

(5)网络与身份

RMI/JMX 绝不要对公网;管理口鉴权;进程权限最小。

(6)检测

流量中识别 Java 序列化魔数打入业务参数;RASP 监控反序列化敏感调用;主机侧看 Java 进程起谁。

5. 授权测试怎么做

  • 找 Base64/ac ed 特征入口;
  • 在授权环境评估是否可构造危害(点到为止);
  • 报告写清入口、JDK/组件信息、修复为"移除反序列化 + 过滤 + 升级";
  • 不在报告附可用攻击链细节给无关方扩散。

6. Java 线一句话

不可信 readObject = 按 RCE 候选处理;用白名单过滤与淘汰原生序列化双管齐下。


三、PHP 线:unserialize 与魔术方法的剧场

1. 常见入口

  • unserialize($_COOKIE/$_POST/...)
  • 缓存、队列里存了 PHP 序列化串;
  • Session 特定 handler;
  • 历史问题:Phar 元数据反序列化(文件操作触发)------与"路径可控"组合很恶心。

PHP 序列化字符串常带 O:4:"Class":... 这类形态,较好认。

2. 利用机制直觉

PHP 对象在反序列化/销毁时会调魔术方法:__wakeup__destruct__toString 等。

若某个类的魔术方法里存在危险操作(删文件、写文件、调驱动),攻击者通过精心构造对象属性,把控制流拐过去。

流行 CMS、框架、插件里若存在"材料类",就会形成公开链;升级插件与框架因此至关重要。

3. Phar 反序列化(要点)

对某些文件函数传入 phar:// 路径时,可能解析 Phar 文件并反序列化元数据。

于是:文件上传(内容可控)+ 文件操作(路径可控) 也能间接触发反序列化。

修法:升 PHP;限制 phar;用户输入禁止进危险文件函数;上传内容当不可信二进制。

4. 防御讲透

(1)禁止反序列化用户输入

需要结构化数据 → json_decode + 校验字段。

(2)allowed_classes 选项

unserialize 的 options 限制允许类(版本需支持)。白名单尽量空或仅少数 DTO,且 DTO 无危险魔术方法。

(3)升级 CMS/框架/插件

PHP 反序列化事故大量出在应用层材料,不单是语言本身。

(4)Session 与缓存

别把不可信数据塞进会自动反序列化的存储。

(5)检测

参数中出现 O:/a: 序列化特征;异常文件操作与 phar://

5. 授权测试怎么做

  • unserialize;抓包找序列化形态;
  • 评估是否用户可控;
  • 结合版本与组件查已知问题(授权范围内);
  • 修复验证:入口改为 JSON,或 allowed_classes 生效。

6. PHP 线一句话

unserialize 用户数据默认按代码执行风险看;JSON 化 + 类白名单 + 插件升级。


四、Python 线:pickle 不配得到信任

1. 常见入口

  • pickle.loads / cPickle
  • 有时包在 Base64、Redis 缓存、任务队列(早期 celery 配置不当等);
  • yaml.load 使用不安全 Loader(偏 YAML 反序列化,常与 pickle 同等对待讲 RCE);
  • shelve、某些 RPC。

2. 为什么说 pickle "设计上就能跑代码"

pickle 协议允许对象在还原时执行重建逻辑(例如通过 __reduce__ 返回可调用对象与参数)。

因此:对不可信数据 pickle.loads ≈ 远程代码执行,不是夸大,是模型问题。

官方文档长期以来也提示:pickle 不安全,不要用于不可信数据。这条被忽略的次数令人心碎。

3. YAML 的坑

yaml.load 无 SafeLoader 时,历史版本可构造危险对象。

应使用 safe_load 或明确 SafeLoader

JSON 能解决的别用 YAML 动态类型。

4. 防御讲透

(1)不可信数据禁用 pickle

换 JSON;需要类型时手动建模(pydantic 等)校验。

(2)缓存与队列

Redis 里别塞 pickle;用 JSON 消息。历史系统迁移要切格式并拒老数据或签名。

(3)签名若必须用 pickle

HMAC 校验完整性 + 仅内网 + 仍要知道:密钥泄漏则仍 RCE。签名是缓解不是根治。

(4)yaml.safe_load

全局搜 yaml.load,改安全加载。

(5)检测

代码扫描 pickle.loadsyaml.load(;运行时监控。

5. 授权测试怎么做

  • 代码审计优先,比盲打高效;
  • 发现入口即高危;
  • 修复对照:JSON + 校验,或删除接口。

6. Python 线一句话

pickle 只吃你自己产的、同信任域的数据;否则不要 loads。


五、三条线对照总表

Java PHP Python
典型 API readObject unserialize pickle.loads
危险来源 入口 + classpath 链 魔术方法 + 应用类 协议本身可执行
额外坑 RMI/JMX、过滤绕过 Phar 间接触发 yaml.load
主修复 停用原生序列化 + 白名单过滤 JSON + allowed_classes 禁用 pickle + JSON
特征 ac ed / rO0 O:数字: pickle 二进制/协议头

六、"序列化"家族的近亲(避免漏报)

  • .NET BinaryFormatter 等(同样危险默认值故事);
  • Android 某些 Parcel/Intent 问题(另一专题);
  • Jackson/Fastjson 等 多态类型处理不当导致的"反序列化貌" RCE(autotype)------不是 Java 原生序列化,但报告常写反序列化;修法是禁 autoType、升级、白名单。

若写企业规范,建议标题用 "不安全反序列化与危险类型恢复",把 JSON autotype、pickle、Java 原生、PHP unserialize 放进同一制度。


七、从漏洞到 RCE 的共用叙事

text 复制代码
用户可控字节进入反序列化入口
  → 恢复对象图 / 触发还原逻辑
  → 利用链触达危险函数
  → 命令执行 / 写文件 / JNDI / 读密钥
  → 权限与横向

打断:

  • 入口消亡或改安全格式;
  • 过滤与升级打断链;
  • 权限与网络减损。

八、组织级落地

  1. 制度:禁止公网接口原生反序列化;例外需架构签字。
  2. 扫描:Semgrep/IDE 插件规则扫危险 API。
  3. 依赖:组件漏洞进补丁 SLA(本系列补丁文)。
  4. 运行时:RASP/EDR 关注反序列化后起进程。
  5. 培训:用"三条线各一个靶场 Demo"做内训,比只念 CVE 号有效。

九、收尾

Java 线教你:classpath 里的类都是弹药,入口要关,过滤要白名单。

PHP 线教你:魔术方法与 Phar 会把"读文件/上传"变成反序列化。

Python 线教你:pickle 从根上就不适合处理不可信数据。

三条线方言不同,母语只有一句:

反序列化的是权力,不是数据;
不可信的输入,不配得到这份权力。

今晚若只做一件事:在三个主要后端仓库分别搜 readObject/ObjectInputStreamunserializepickle.loads/yaml.load,把命中列成表,标出数据来源是否用户可控。

这张表,就是你们离"反序列化 RCE"真实距离的地图。


附:安全替代

需求 更安全常见选择
API 传结构化数据 JSON + schema 校验
缓存 JSON/MessagePack 等数据格式
RPC 明确 IDL 的 gRPC/PB 等
配置 JSON/TOML + 校验,慎用不安全 YAML
相关推荐
江畔柳前堤1 小时前
Function Calling 与 Tool Calling:从认知到工程的全景深度解析
开发语言·网络·人工智能·深度学习·算法·机器学习·php
2601_949950631 小时前
Java 面试准备,用练题簿把“背过八股”变成“真正会答”
java·开发语言·面试·刷题·小程序推荐
努力的小Qin1 小时前
梯度下降如何实现参数优化:从线性回归到 Sigmoid 分类
人工智能·python·神经网络
LabVIEW开发1 小时前
LabVIEW 通过以太网控制 Yokogawa WT3000 功率分析仪:tmctl.dll 与原始套接字方案
网络·labview·labview知识·labview功能·labview程序
strength_zhou20131 小时前
Python检查MongoDB索引列中的字段是否存在
python·mongodb
听你说322 小时前
推进具身智能安全评测:丈八科技与季华实验室达成战略合作
人工智能·科技·安全
zander2582 小时前
LeetCode 739:每日温度——为什么单调栈要持续弹出
开发语言·python·算法
無限進步D2 小时前
Java 函数式编程(Lambda)
java·开发语言
rannn_1112 小时前
【力扣hot100】链表专题下|138、148、23、146
java·算法·leetcode·链表·开发