前言
此篇文章将分析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.
- 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.
- 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.
- RPC is language neutral unlike RMI, which is limited to java
- RMI is little bit slower to RPC
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
- 下载好Apache Dubbo后用intellij打开http module
http-provider.xml中修改端口号,zookeeper会占用8080端口
pom.xml中修改dubbo version为2.7.3
在dependencies中也需要添加version,否则会失效(这里卡了很久)。
Zookeeper
下载好zookeeper,在conf中新建一个zoo.cfg,并将zoo_sample.cfg的内容复制到zoo.cfg中,可以自行修改配置
然后运行bin\zkServer.cmd
漏洞分析
运行HttpConsumer之后,发现实际上访问了
我们使用postman来验证一下:
用debug模式运行httpProvider,再使用postman发送get请求
http://127.0.0.1:8081/org.apache.dubbo.samples.http.api.DemoService
发现请求发出后,会被断在DispatcherServlet#service处
根据servlet相关知识我们知道,请求会被分类封装(如get请求、post请求),然后被发给指定的servlet
我们继续跟进handle方法
可以发现会检测到request的请求方法,如果是get,则返回500错误。因此我们需要尝试将其改为post
请求发送后再次断在刚才的地方
继续跟进
1 | public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { |
1 | protected RemoteInvocation readRemoteInvocation(HttpServletRequest request) throws IOException, ClassNotFoundException { |
继续跟进
1 | protected RemoteInvocation readRemoteInvocation(HttpServletRequest request, InputStream is) throws IOException, ClassNotFoundException { |
下面的this.doReadRemoteInvocation(ois);
并未被执行,为什么呢?
因为我们并未在post请求体中放入数据,无法获取到输入流
看看doReadRemoteInvocation方法
1 | protected RemoteInvocation doReadRemoteInvocation(ObjectInputStream ois) throws IOException, ClassNotFoundException { |
漏洞复现
在pom.xml中添加
1 | <dependency> |
重新运行HttpProvider
使用ysoserial生成cc4 payload
使用postman:
发送请求后成功弹出计算器:
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/