Apache Dubbo系列漏洞分析
2023-06-16 14:30:36 # Web Security # Apache

前言

此篇文章将分析apache dubbo系列漏洞,在复现的时候花了些时间,因为国内好多文章都是照搬,这里将自己的复现过程全部写出来,也是为以后避坑。

什么是RPC

RPC全称为Remote Procedure Calls,翻译为远程过程调用,常用于分布式。

关于RPC和RMI的区别:

*Remote Procedure Call (RPC)* is a inter process communication which allows calling a function in another process residing in local or remote machine.

*Remote method invocation (RMI)* is an API, which implements RPC in java with support of object oriented paradigms.

  1. You can think of invoking RPC is like calling a C procedure. RPC supports primitive data types where as RMI support method parameters/return types as java objects.
  2. RMI is easy to program unlike RPC. You can think your business logic in terms of objects instead of a sequence of primitive data types.
  3. RPC is language neutral unlike RMI, which is limited to java
  4. RMI is little bit slower to RPC

img

Apache Dubbo Introduction

Apache Dubbo是开源的Alibaba RPC和微服务框架,其包含以下组成:

  • Provider
  • Container
  • Consumer
  • Registry - 分布式中的注册中心,返回list of providers to a consumer
  • Monitor

CVE 2019-17564

环境搭建

  • 2.7.0 <= Apache Dubbo <= 2.7.4
  • 2.6.0 <= Apache Dubbo <= 2.6.7
  • Apache Dubbo = 2.5.x
  • Zookeeper
  1. 下载好Apache Dubbo后用intellij打开http module

image-20220209142533883

http-provider.xml中修改端口号,zookeeper会占用8080端口

image-20220209143024881

pom.xml中修改dubbo version为2.7.3

image-20220209143323692

image-20220209143334541

在dependencies中也需要添加version,否则会失效(这里卡了很久)。

Zookeeper

下载好zookeeper,在conf中新建一个zoo.cfg,并将zoo_sample.cfg的内容复制到zoo.cfg中,可以自行修改配置

然后运行bin\zkServer.cmd

漏洞分析

运行HttpConsumer之后,发现实际上访问了image-20220209163525769

我们使用postman来验证一下:

用debug模式运行httpProvider,再使用postman发送get请求

http://127.0.0.1:8081/org.apache.dubbo.samples.http.api.DemoService

image-20220209163631458

发现请求发出后,会被断在DispatcherServlet#service处

image-20220209163709134

根据servlet相关知识我们知道,请求会被分类封装(如get请求、post请求),然后被发给指定的servlet

我们继续跟进handle方法

image-20220209163850376

可以发现会检测到request的请求方法,如果是get,则返回500错误。因此我们需要尝试将其改为post

image-20220209163937359

请求发送后再次断在刚才的地方

image-20220209164014482

继续跟进

1
2
3
4
5
6
7
8
9
10
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
//在此处F7跟进,看他是如何read Remote Invocation的
RemoteInvocation invocation = this.readRemoteInvocation(request);
RemoteInvocationResult result = this.invokeAndCreateResult(invocation, this.getProxy());
this.writeRemoteInvocationResult(request, response, result);
} catch (ClassNotFoundException var5) {
throw new NestedServletException("Class not found during deserialization", var5);
}
}
1
2
3
protected RemoteInvocation readRemoteInvocation(HttpServletRequest request) throws IOException, ClassNotFoundException {
return this.readRemoteInvocation(request, request.getInputStream());
}

继续跟进

1
2
3
4
5
6
7
8
9
10
11
12
13
14
protected RemoteInvocation readRemoteInvocation(HttpServletRequest request, InputStream is) throws IOException, ClassNotFoundException {
//此处会获取输入流,往下跟后发现抛出了异常
ObjectInputStream ois = this.createObjectInputStream(this.decorateInputStream(request, is));

RemoteInvocation var4;
try {
//此处read Remote Invocation
var4 = this.doReadRemoteInvocation(ois);
} finally {
ois.close();
}

return var4;
}

image-20220209164418884

下面的this.doReadRemoteInvocation(ois);并未被执行,为什么呢?

因为我们并未在post请求体中放入数据,无法获取到输入流

看看doReadRemoteInvocation方法

image-20220209165553503

1
2
3
4
5
6
7
8
9
protected RemoteInvocation doReadRemoteInvocation(ObjectInputStream ois) throws IOException, ClassNotFoundException {
//反序列化触发
Object obj = ois.readObject();
if (!(obj instanceof RemoteInvocation)) {
throw new RemoteException("Deserialized object needs to be assignable to type [" + RemoteInvocation.class.getName() + "]: " + ClassUtils.getDescriptiveType(obj));
} else {
return (RemoteInvocation)obj;
}
}

漏洞复现

在pom.xml中添加

1
2
3
4
5
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>

重新运行HttpProvider

使用ysoserial生成cc4 payload

使用postman:

image-20220209165848037

发送请求后成功弹出计算器:

image-20220209165906408

Reference

https://stackoverflow.com/questions/2728495/what-is-the-difference-between-java-rmi-and-rpc

https://itzone.com.vn/en/article/an-introduction-to-apache-dubbo-and-cve-2019-17564-analysis/