Jdk7u21 反序列化漏洞Gadget原理
字数 1977 2025-08-29 08:31:53
JDK7u21 反序列化漏洞 Gadget 原理分析
0x00 漏洞概述
JDK7u21 反序列化漏洞是一个经典的 Java 反序列化安全问题,利用 Java 序列化机制中的特定类组合(Gadget Chain)实现远程代码执行。该漏洞的核心在于利用 Java 动态代理机制和哈希碰撞技巧,通过精心构造的序列化数据触发恶意代码执行。
0x01 前置知识
1. Java 动态代理机制
- 动态代理类实现了 InvocationHandler 接口
- 代理对象方法调用会被转发到 InvocationHandler 的 invoke 方法
- 本例中使用的是 AnnotationInvocationHandler 作为 InvocationHandler
2. TemplatesImpl 类
- 位于 javax.xml.transform 包中
- 可以加载字节码并执行
- 通过 getOutputProperties() 方法可以触发字节码执行
3. Java 序列化机制
- 反序列化时会调用对象的 readObject 方法
- HashSet/LinkedHashSet 反序列化时会重新计算哈希值并比较元素
0x02 Gadget 组成
整个利用链由以下几个关键部分组成:
- LinkedHashSet:作为反序列化的入口点
- TemplatesImpl 对象:包含恶意字节码
- 代理对象:
- 实现 Templates 接口
- 使用 AnnotationInvocationHandler 作为 InvocationHandler
- memberValues 设置为特殊构造的 Map
0x03 利用链详细分析
1. 构造恶意序列化数据
LinkedHashSet set = new LinkedHashSet();
// 1. 添加包含恶意代码的 TemplatesImpl 对象
set.add(templatesImpl);
// 2. 创建代理对象
Map map = new HashMap();
map.put("f5a5a608", templatesImpl);
InvocationHandler handler = new AnnotationInvocationHandler(Templates.class, map);
Templates proxy = (Templates) Proxy.newProxyInstance(
Templates.class.getClassLoader(),
new Class[]{Templates.class},
handler
);
// 3. 添加代理对象到集合
set.add(proxy);
2. 反序列化触发流程
-
LinkedHashSet.readObject():
- 反序列化时调用 HashSet 的 readObject 方法
- 创建 HashMap 来存储 Set 中的元素
- 调用 HashMap.put() 方法添加元素
-
HashMap.put() 关键逻辑:
for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { // ... } }- 需要满足两个条件:
e.hash == hash:哈希值相等key.equals(k):对象相等或 equals 方法返回 true
- 需要满足两个条件:
-
哈希碰撞技巧:
- 代理对象的 hashCode() 会调用 AnnotationInvocationHandler.invoke()
- invoke() 调用 hashCodeImpl() 方法:
private int hashCodeImpl() { int result = 0; for (Map.Entry<String, Object> e : memberValues.entrySet()) { result += (127 * e.getKey().hashCode()) ^ memberValueHashCode(e.getValue()); } return result; } - "f5a5a608".hashCode() 为 0,因此:
result = (127 * 0) ^ templatesImpl.hashCode() = templatesImpl.hashCode() - 这样就实现了
proxy.hashCode() == templatesImpl.hashCode()
-
equals 方法触发恶意代码:
- 当
e.hash == hash条件满足后,会调用key.equals(k) - 代理对象的 equals 方法会调用 AnnotationInvocationHandler.invoke()
- invoke() 调用 equalsImpl() 方法:
private Boolean equalsImpl(Object proxy, Object other) { for (Method method : getMemberMethods()) { Object thisVal = method.invoke(proxy); Object otherVal = method.invoke(other); // ... } } - 这会触发 TemplatesImpl 的 getOutputProperties() 方法,执行恶意字节码
- 当
0x04 关键点总结
-
AnnotationInvocationHandler 的核心作用:
- 作为动态代理的 InvocationHandler
- 通过 invoke 方法控制代理对象的行为
- 提供了 equalsImpl 和 hashCodeImpl 的关键实现
-
哈希碰撞技巧:
- 精心选择 "f5a5a608" 作为 key,其 hashCode() 为 0
- 使得 proxy.hashCode() == templatesImpl.hashCode()
- 绕过 HashMap.put() 的第一个条件检查
-
执行链触发点:
- LinkedHashSet/HashSet 反序列化
→ HashMap.put()
→ equals 比较
→ 代理对象方法调用
→ AnnotationInvocationHandler.invoke()
→ equalsImpl()
→ TemplatesImpl.getOutputProperties()
→ 恶意代码执行
- LinkedHashSet/HashSet 反序列化
0x05 防御措施
- 升级 JDK 到最新版本
- 使用安全管理器限制反序列化操作
- 使用白名单机制控制可反序列化的类
- 替换默认的序列化机制(如使用 JSON 等替代)
0x06 参考实现
完整 PoC 通常包含以下组件:
- 生成恶意字节码的工具(如 ASM、javassist)
- 构造 TemplatesImpl 对象的代码
- 创建代理对象和 AnnotationInvocationHandler 的代码
- 序列化 LinkedHashSet 的代码
注意:本文仅用于技术研究,请勿用于非法用途。