最简单的jdk原生链8u20
字数 2251 2025-08-27 12:33:22
JDK 8u20反序列化漏洞利用分析
漏洞背景
JDK 8u20反序列化漏洞是对7u21漏洞的一种绕过利用方式,主要利用了Java序列化机制中的两个关键特性:
- 双层try/catch结构
- TC_REFERENCE机制
核心利用原理
1. 双层try/catch机制
基本原理:
- 当内层try块抛出异常时,如果外层catch能够捕获该异常类型,程序不会立即终止
- 外层catch如果没有再次throw,程序可以继续执行后续代码
在漏洞中的利用:
AnnotationInvocationHandler#readObject中先执行s.defaultReadObject(),然后才进行type判断- 利用双层try/catch可以让
AnnotationInvocationHandler对象被还原,即使type判断后的代码无法执行也不影响利用
2. TC_REFERENCE机制
基本原理:
- Java序列化/反序列化会为每个对象分配一个Handler值(类似索引)
- 当同一个对象被多次引用时,后续引用会直接使用第一次生成的Handler值
示例说明:
Foo f = new Foo();
oos.writeObject(f);
oos.writeObject(f); // 这里第二个f会存储第一个f的Handler
Handler值规则:
- 第一个Handler值为
0x7E0000(十进制8257536) - 后续每个新Handler值递增1(第二个为8257537,以此类推)
利用链构造
关键组件
-
BeanContextSupport:
- 其
readObject()调用了readChildren(ois) - 内部的catch块满足双层try/catch条件
- 可以包裹
AnnotationInvocationHandler对象
- 其
-
TemplatesImpl:
- 用于hash碰撞,与7u21利用方式相同
-
Proxy对象:
- 代理类与
BeanContextSupport包裹的是同一个对象
- 代理类与
利用步骤
- 让
BeanContextSupport包裹AnnotationInvocationHandler对象并放在第一位 TemplatesImpl放在第二位,用于hash碰撞Proxy放在第三位,代理类与BeanContextSupport包裹的是同一个对象
执行流程:
- 先反序列化
BeanContextSupport对象,还原AnnotationInvocationHandler - Proxy通过Handler调用已还原的
AnnotationInvocationHandler对象 - 后续流程与7u21相同
问题分析与解决
报错问题分析
报错现象:
- 反序列化时出现
readInt()报错 - 根源在于
readBlockHeader()返回-1导致in.read()为-1
深层原因:
-
defaultDataEnd标志:- 当类没有
writeObject()方法时,defaultDataEnd = true - 但这不是根本原因,因为测试发现没有
writeObject()的类也能正常反序列化
- 当类没有
-
关键问题:
AnnotationInvocationHandler的readObject报错导致退出- 未能执行
defaultDataEnd = false的重新赋值 - 导致
readInt()报错
解决方案:
- 修改
flags从0x02改为0x03(添加SC_WRITE_METHOD标志) - 删除序列化数据中的null值,确保数据对齐
数据对齐问题
为什么删除null值:
- 序列化数据中:
TC_NULL对应null值TC_BLOCKDATA对应int值
- 需要让
TC_BLOCKDATA提前 - 删除
TC_NULL不会影响后续反序列化,因为:readInt()标志着BeanContextSupport反序列化结束- 紧接着要反序列化下一个对象(
TemplatesImpl)
完整POC构造
关键点:
- 使用
BeanContextSupport包裹AnnotationInvocationHandler - 设置
TemplatesImpl用于hash碰撞 - 使用Proxy代理同一个对象
- 修改flags添加
SC_WRITE_METHOD - 删除序列化数据中的null值确保对齐
JDK 8u20修复
修复方式:
- 限制反射调用的方法,只允许以下三种:
equalshashCodetoString
- 这样就不能调用危险的sink点方法
调试技巧
-
序列化数据分析:
- 使用工具如zkar或SerializationDumper
- 关注
@ObjectAnnotation和@ClassDescFlags
-
定位问题:
- 通过前后
pos值的变化定位序列化数据中的位置 - 关注
defaultDataEnd标志的变化
- 通过前后
-
异常跟踪:
- 注意异常抛出后是否会进入catch块
- 确保外层catch能捕获内层抛出的异常类型
总结
JDK 8u20反序列化漏洞利用了两个关键机制:
- 通过双层try/catch绕过
AnnotationInvocationHandler的type检查 - 利用TC_REFERENCE机制复用已反序列化的对象
成功利用需要:
- 精心构造的序列化数据
- 正确处理数据对齐问题
- 理解Java序列化/反序列化的内部机制
通过深入分析序列化数据和调试,可以完全理解漏洞利用的每个细节,包括为什么需要删除某些数据以及如何确保利用链的完整执行。