Dubbo反序列化漏洞(CVE-2023-23638)原理分析
2023-03-31 13:04:00

0x00、前言

Dubbo反序列化漏洞(CVE-2023-23638)复现与分析

0x01、Dubbo描述

Apache Dubbo 是一款高性能、轻量级的开源 Java 服务框架。Apache Dubbo提供了六大核心能力:面向接口代理的高性能RPC调用,智能容错和负载均衡,服务自动注册和发现,高度可扩展能力,运行期流量调度,可视化的服务治理与运维。

0x02、漏洞描述

Dubbo 泛化调用时由于反序列化检查机制实现存在缺陷,可访问目标服务的攻击者可能在服务提供方上执行恶意代码。利用此漏洞需知道接口全限定名、方法名、入参及返参类型。攻击者成功利用此漏洞可造成远程代码执行。

0x03、漏洞范围

Apache Dubbo 2.7.x <= 2.7.21
Apache Dubbo 3.0.x <= 3.0.13
Apache Dubbo 3.1.x <= 3.1.5

0x04、环境搭建

环境需要zookeeper+dubbo

zookeeper搭建:
zookeeper官方下载:https://zookeeper.apache.org/releases.html
需要修改config,主要是添加dataDir/dataLogDir数据和日志路径

直接运行bin目录下的zkServer.cmd即可

dubbo环境搭建:
可参考dubbo环境搭建

或者使用已经搭建好的均可:
https://github.com/lz2y/DubboPOC/
https://github.com/X1r0z/CVE-2023-23638

本环境使用https://github.com/lz2y/DubboPOC/ 导入的漏洞环境

<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>2.7.21</version>

最后更新下maven即可

0x05、漏洞复现

可选择jdbcRowSetImpl链加载ldap进行复现

触发位置

0x06、原理分析

泛型调用

dubbo提供程序接口公开的任意方法的泛型调用,通过解码流数据后由GenericFilter类进行处理

其中要求:

  • inv对象的方法名等于$invoke或者$invokeAsync
  • inv参数不能为空,参数数量为3个参数并且要求不继承GenericService
if ((inv.getMethodName().equals($INVOKE) || inv.getMethodName().equals($INVOKE_ASYNC))
&& inv.getArguments() != null
&& inv.getArguments().length == 3
&& !GenericService.class.isAssignableFrom(invoker.getInterface())) {
String name = ((String) inv.getArguments()[0]).trim();
String[] types = (String[]) inv.getArguments()[1];
Object[] args = (Object[]) inv.getArguments()[2];

获取inv其中的nametypesargs属性对应inv对象的三个参数(方法名、方法类型、方法参数)

接着调用ReflectUtils.findMethodByMethodSignature方法判断改对象是否是系统服务提供的方法,不为则抛出异常,这也是实现的必要条件之一

因此payload中会在流中写入服务提供端的调用方法

接下来根据generic属性进行不同的序列化操作

方式一:SerializeClassChecker绕过黑名单执行setter

其中如果generic属性为raw.return,则会调用PojoUtils.realize方法进行反序列化操作

该方法会循环数组对象调用realize方法,进而调用realize0方法


realize0方法中判断生成的pojo对象,其中有判断pojo对象为泛型的情况

pojo对象为泛型,则会从pojo对象中获取keyclass的值作为将实例化出来的类名,并经过SerializeClassChecker.getInstance().validateClass检查该类名是否在序列化类检查器的黑名单中

默认情况序列化检查OPEN_CHECK_CLASS是开启的,因此会对类名进行黑名单检查,会循环遍历类名前缀是否匹配黑名单进行抛出异常



因此关闭序列化检查也是实现的必要条件之一,解决方法可重新传递一个Instance变量值去替换重新生成new SerializeClassChecker()序列化检查器,让Instance不为空,即在调用getInstance方法时则不会new SerializeClassChecker(),从而规避黑名单检查


替换方式:

hashmap.put("class", "org.apache.dubbo.common.utils.SerializeClassChecker");
hashmap.put("CLASS_DESERIALIZE_BLOCKED_SET", new ConcurrentHashSet<>());
HashMap<String, Object> scc = new HashMap<>();
scc.put("class", "org.apache.dubbo.common.utils.SerializeClassChecker");
scc.put("INSTANCE", hashmap);

回到泛型处理来,规避检查后,将className通过反射获取的类返回给type

然后对type进行类型判断进入不同的处理方法,依次判断type是否是枚举类、是否是map类的子类/是否是Object类型、是否是接口类型

if (type.isEnum()) {

if (Map.class.isAssignableFrom(type) || type == Object.class) {

else if (type.isInterface()) {

如果都不是,则直接实例化type对象,通过读取map中的键值对,通过getSetterMethod方法获取到type对象中存在set前缀开头的key名称的方法

如果找不到type类对象中存在set前缀开头的key名称的方法,则可以设置任意field

如果能找到set前缀开头的key名称的方法,则直接通过反射获取该方法并执行

除上述执行方法外,还有一类,通过开启NATIVE_JAVA执行方式,GenericFilter#invoke方法中

方式二:java_native绕过执行原生反序列化

箭头为上述的方法,红框为使用native java方法

还有一类使用Native java方式,模式情况是禁止的,该方法是直接使用原生反序列化直接反序列化数据,ENABLE_NATIVE_JAVA_GENERIC_SERIALIZE值默认为false

若开启native-java执行方式,则直接调用反序列化

其中configuration是从系统环境配置读取的信息

下面是configuration读取系统配置的调用信息




现在需要利用一个类,能对Property系统环境配置里的dubbo.security.serialize.generic.native-java-enable属性设置为true

通过ConfigUtils类存在setProperties方法,可利用上述的泛型引用反射执行setter方法去赋值properties属性,将dubbo.security.serialize.generic.native-java-enable属性设置为true


可通过Properties类的setProperty方法将dubbo.security.serialize.generic.native-java-enable=true放入hashmap

再将Properties类对象传递入ConfigUtilssetProperties方法去实现修改配置属性

修改完属性就能传递payload通过java-native去执行反序列化攻击。

总结

方式一:

  • 设置generic属性为raw.return
  • 重新传递一个Instance变量值去替换重新生成new SerializeClassChecker()序列化检查器规避黑名单
  • 通过setter调用类方法,通过反射执行方法。

方式二:

  • 设置generic属性为raw.return
  • Properties类对象传递入ConfigUtilssetProperties方法去实现修改配置dubbo.security.serialize.generic.native-java-enable属性为true
  • 通过Java-native执行原生反序列化

效果区别:
方式一只能使用通过setter方法调用实现的类方法,只有利用链通过setter方法触发的才能实现,例如jdbcRowSetImpl链,通过setter和反射设置setDataSourceName为jndi加载地址,再通过setter和反射执行setAutoCommit方法实现jndi调用。

方式二可使用任意方法的序列化,兼容利用链更多,能使用更多的利用链如存在cc组件,可使用cc链进行攻击。

0x07、漏洞修复

目前官方已发布Apache Dubbo安全版本,建议尽快升级至安全版本:

Apache Dubbo 2.7.x >= 2.7.22
Apache Dubbo 3.0.x >= 3.0.14
Apache Dubbo 3.1.x >= 3.1.6

https://github.com/apache/dubbo/releases

修复方式:

  • 主要新增了可序列化类判断,对type进行校验

新增checkSerializable判断和判断该类是否是可序列化的类

其中checkSerializable属性默认为true即开启序列化检查

同时在上文利用提到的方式一用的SerializeClassChecker类来规避黑名单以及方式二使用的ConfigUtils类来修改属性,这两个类都未实现序列化接口,因此都无法通过是否实现序列化的if检查

使用方式一执行SerializeClassChecker类来规避黑名单直接抛出该类不允许序列化异常

因此也自然无法使用JDBC链

0x08、参考链接

https://nox.qianxin.com/article/540
https://github.com/apache/dubbo/commit/4f664f0a3d338673f4b554230345b89c580bccbb
https://github.com/lz2y/DubboPOC
https://github.com/X1r0z/CVE-2023-23638