CVE-2019-17564:Apache Dubbo反序列化漏洞分析
2020-02-14 17:34

报告编号:B6-2020-021401

报告来源:360-CERT

报告作者:360-CERT

更新日期:2020-02-14

0x00 漏洞背景

2020年2月12日,360CERT监测到Apache Dubbo官方发布了CVE-2019-17564漏洞通告,漏洞等级中危。

Apache Dubbo是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。Apache Dubbo支持多种协议,官方默认为 Dubbo 协议,当用户选择http协议进行通信时,Apache Dubbo 在接受来自消费者的远程调用的POST请求的时候会执行一个反序列化的操作,由于没有任何安全校验,于是可以造成反序列化执行任意代码

0x01 架构

enter description here

节点角色说明

节点 角色说明
Provider 暴露服务的服务提供方
Consumer 调用远程服务的服务消费方
Registry 服务注册与发现的注册中心
Monitor 统计服务的调用次数和调用时间的监控中心
Container 服务运行容器

调用关系说明

  1. 服务容器负责启动,加载,运行服务提供者。
  2. 服务提供者在启动时,向注册中心注册自己提供的服务。
  3. 服务消费者在启动时,向注册中心订阅自己所需的服务。
  4. 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
  5. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
  6. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

0x02 环境搭建

官方已经给我们提供了基本样例

git clone https://github.com/apache/dubbo-samples.git
cd dubbo-samples/java/dubbo-samples-http

zookeeper注册中心下载:https://zookeeper.apache.org/releases.html

简单列举服务提供者用到的文件:

公共接口:DemoService.java

package org.apache.dubbo.samples.http.api;

public interface DemoService {

    String sayHello(String name);

}

配置文件: spring/http-provider.xml

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <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" 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>

服务提供者:HttpProvider.java

package org.apache.dubbo.samples.http;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.concurrent.CountDownLatch;

public class HttpProvider {

    public static void main(String[] args) throws Exception {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/http-provider.xml");
        context.start();

        System.out.println("dubbo service started");
        new CountDownLatch(1).await();
    }

}

服务消费者: HttpConsumer.java

package org.apache.dubbo.samples.http;

import org.apache.dubbo.samples.http.api.DemoService;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class HttpConsumer {

    public static void main(String[] args) throws Exception {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/http-consumer.xml");
        context.start();

        DemoService demoService = (DemoService) context.getBean("demoService");
        String result = demoService.sayHello("world");
        System.out.println(result);
    }
}
  1. 启动zookeeper注册中心
  2. 运行服务提供者
  3. 运行服务消费者 enter description here

0x03 漏洞验证

由于官方样例提供的版本是最新版,无法验证漏洞,于是我们需要还原漏洞版本,修改/dubbo-samples/java/dubbo-samples-http/pom.xmldubbo.version2.7.3

    <properties>
        <source.level>1.8</source.level>
        <target.level>1.8</target.level>
        <!--修改版本为2.7.3-->
        <dubbo.version>2.7.3</dubbo.version>
        ...

exp构造

根据 https://www.mail-archive.com/dev@dubbo.apache.org/msg06225.html 发布的漏洞信息来看,造成漏洞的原因应该是在 POST 请求的时候会执行一个反序列化的操作,那么我们 POST 任意数据,并在处理分派请求的地方下断,/org/apache/dubbo/remoting/http/servlet/DispatcherServlet.java enter description here 任意发送一个POST请求,这里击中了断点 enter description here 跟进handle enter description here 这里需要注意的是,首先初始化了一个skeletonMap,然后会判断我们请求的path是否在Map里,如果不在,skeleton就会返回null值,这样调用到skeleton.handleRequest的时候就会报java.lang.NullPointerException的错误 enter description here 于是我们请求的path为/org.apache.dubbo.samples.http.api.DemoService,这样就能获取到skeleton的值 enter description here 继续跟进readRemoteInvocation enter description here enter description here enter description here 这里需要从request里得到inputStream,否则无法进入造成漏洞的关键方法:doReadRemoteInvocation 最后看到doReadRemoteInvocation,对我们的数据进行了反序列化,并且没有做任何安全校验 enter description here

基于commons-collections-3.2gadgets enter description here

0x04 版本更新

skeletonMap进行了修改,在获取skeleton之后就会调用JsonRpcBasicServer.hanldeJsonRpcBasicServerJsonRpcServer的父类,在该类中没有反序列化的危险操作 enter description here enter description here

0x05 修复建议

0x06 相关空间测绘数据

360安全大脑-Quake网络空间测绘系统通过对全网资产测绘

enter description here 可以发现 Apache Dubbo 框架在国内得到广泛的使用。

0x07 产品侧解决方案

360城市级网络安全监测服务

360安全大脑的QUAKE资产测绘平台通过资产测绘技术手段,对该类漏洞进行监测,请用户联系相关产品区域负责人获取对应产品。

360AISA全流量威胁分析系统

360AISA基于360海量安全大数据和实战经验训练的模型,进行全流量威胁检测,实现实时精准攻击告警,还原攻击链。

目前产品具备该漏洞/攻击的实时检测能力。

0x08 参考链接

  1. https://www.mail-archive.com/dev@dubbo.apache.org/msg06225.html
  2. http://dubbo.apache.org/zh-cn/docs/user/quick-start.html
  3. https://cloud.tencent.com/developer/article/1468001