前言
由于前段时间研究过一阵的hessian,在看到这个洞出来的时候就去关注了。因为上次的漏洞之后,hessian直接改为反序列化只能继承序列化接口的类了,之后基本就没有什么研究价值了。仔细一看这个洞是dubbo另外一个功能的漏洞,正好复现分析一下,顺便熟悉一下dubbo
dubbo
Dubbo是一款Java RPC框架,致力于提供高性能的RPC远程服务调用方案。Dubbo 作为主流的微服务框架之一,为开发人员带来了非常多的便利。Dubbo主要提供了3大核心功能:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。这次的漏洞就是远程方法调用中出现的漏洞
dubbo的核心组件
1)注册中心(registry)
生产者在此注册并发布内容,消费者在此订阅并接收发布的内容。
2)消费者(consumer)
客户端,从注册中心获取到方法,可以调用生产者中的方法。
3)生产者(provider)
服务端,生产内容,生产前需要依赖容器(先启动容器)。
4)容器(container)
生产者在启动执行的时候,必须依赖容器才能正常启动(默认依赖的是spring容器),
5)监控(Monitor)
统计服务的调用次数与时间等。
dubbo调用流程
1、服务提供者启动,开启Netty服务,创建Zookeeper客户端,向注册中心注册服务;
2、服务消费者启动,通过Zookeeper向注册中心获取服务提供者列表,与服务提供者通过Netty建立长连接;
3、服务消费者通过接口开始远程调用服务,ProxyFactory通过初始化Proxy对象,Proxy通过创建动态代理对象;
4、动态代理对象通过invoke方法,层层包装生成一个Invoker对象,该对象包含了代理对象;
5、Invoker通过路由,负载均衡选择了一个最合适的服务提供者,在通过加入各种过滤器,协议层包装生成一个新的DubboInvoker对象;
6、再通过交换成将DubboInvoker对象包装成一个Reuqest对象,该对象通过序列化通过NettyClient传输到服务提供者的NettyServer端;
7、到了服务提供者这边,再通过反序列化、协议解密等操作生成一个DubboExporter对象,再层层传递处理,会生成一个服务提供端的Invoker对象;
8、这个Invoker对象会调用本地服务,获得结果再通过层层回调返回到服务消费者,服务消费者拿到结果后,再解析获得最终结果。
这边demo全部参照官方用例https://github.com/apache/dubbo-samples
漏洞分析
先去diff一下代码看看这次修复的地方是哪https://github.com/apache/dubbo/commit/4f664f0a3d338673f4b554230345b89c580bccbb#diff-c9002571855ba4e5eb625b72c81307ed8bf189f5ab757652320cc843738040b7
可以看到他加了一个验证,和hessian一样现在都只能支持继承了序列化的类,一开始我以为是某个没继承的类绕过了黑名单,然后去看了一下漏洞通报,说是dubbo泛化功能出现的问题,然后就去看了一下具体代码实现
具体会调用到org.apache.dubbo.rpc.filter.GenericFilter#invoke
这里提取出数据,对不同的类型用不同的方法进行方法调用
可以看到补丁主要是修复了 PojoUtils.realize 方法和 JavaBeanSerializeUtil.deserialize 方法,这两个类的方法逻辑是一样的 大致都是
1 | 若pojo为Map实例,则从pojo获取key为“class”的值,并通过反射得到class所对应的类type,再判断对象的类型进行下一步处理 |
说白了就是会调用对象的setter方法,去完成对象的实例化。这里的流程都是先反射获取类,然后通过无参构造函数(或空Object)实例化,获取到对象之后再通过setter方法设置相应的值。这里有个关键的地方如果有属性没有相应的setter方法,就会直接反射赋值。
当时调试到这的时候我以为是某个不在黑名单中的类存在可以被利用的setter方法(甚至拿tabby扫了一遍。后面去看了一下更新之后的黑名单发现没有变化,
原本在这陷入僵局,突然想到这里有getField可以直接赋值,看一下黑名单验证的具体代码
如果可以直接修改这个类的这个属性,那么就可以直接跳过整个验证黑名单的过程。
然后这个位置,必须要设置一个INSTANCE
不然为空会出现新建一个类,就会重置前面的属性。所以创建一个SerializeClassChecker
对象并设置OPEN_CHECK_CLASS
为false,然后让INSTANCE
不为空就可以直接跳过黑名单验证。然后就可以通过常用的com.sun.rowset.JdbcRowSetImpl
来通过jndi造成rce
https://tttang.com/archive/1730/#toc_cve-2021-30179
参照之前这个功能的cve可以写出poc
1 | public static void main(String[] args) throws Exception { |