vlambda博客
学习文章列表

补个漏洞不容易—Dubbo远程命令执行漏洞修复始末

   

“纵观中国开源历史,你真的没法找到第二个像Dubbo一样自带争议和讨论热度的开源项目。”Dubbo的开发人员如是评论自己的“孩子”:Dubbo是一个优秀的RPC框架,是Java项目中卓越的框架之一。同所有的优秀服务提供框架一样,Dubbo也面临的安全问题。从本文从Dubbo今年6月遭遇的远程代码执行漏洞的入手,介绍Dubbo发现漏洞、修复漏洞、被绕过、继续修复的过程,期间涵盖了Dubbo漏洞原理和漏洞利用方式,希望可以借助Dubbo修复漏洞事件为例,来提供企业对重用应用框架安全的关注和重视程度。


补个漏洞不容易—Dubbo远程命令执行漏洞修复始末

01

技术背景



什么是dubbo?

Dubbo是一个优秀的RPC框架,是Java项目中卓越的框架之一。 2011年,它的开源填补了当时生产环境使用的RPC框架的空白,一发布就被广泛采用; 另一方面,它经历了停止维护、重启维护后捐献给Apache基金会、接着又以顶级项目的身份毕业。
如果说最开始重新激活是以阿里巴巴为主导的项目维护投入,那自 Dubbo 加入 Apache 起,它就已经开始成为一个社区主导、社区贡献为主的完全开放的基金会项目。包括阿里巴巴、携程、工商银行、瓜子二手车、网联清算、中通等在内的互联网、传统企业公司,在 Dubbo的使用与社区代码贡献上都有投入。Dubbo社区正变得非常活跃和多样化。
Dubbo的运行过程如图所示:

补个漏洞不容易—Dubbo远程命令执行漏洞修复始末
Dubbo的运行过程:

1.服务提供者Provider启动时会向注册中心把自己的元数据注册上去,如服务IP、端口及需要注册的接口定义等信息;
2.服务消费方Consumer在启动时会从注册中心订阅服务提供方的元数据;
3.而当注册中心数据发生变更时,例如Provider某个节点Down掉,又或者服务提供方进行了扩容、增加了节点,会推送给订阅的Consumer;
4.在Consumer获取到元数据后,Consumer可以发起RPC调用;
5.RPC调用前后会向监控中心上报统计信息。

02

遭遇漏洞



1.Dubbo远程命令执行漏洞


2020年6月22日Apache官方发布了Dubbo 2.7.7版本,其中修复了一个严重的远程代码执行漏洞(CVE-2020-1948),该漏洞允许攻击者使用任意的服务名和方法名发送RPC请求,同时将恶意序列化参数作为Payload,当恶意序列化的参数被反序列化时将执行恶意代码。该漏洞与CVE-2017-3241RMI反序列化漏洞有点类似,都是在远程调用过程中通过方法参数传入恶意序列化对象,服务端在解析参数进反序列化时触发。


补个漏洞不容易—Dubbo远程命令执行漏洞修复始末


2.漏洞原理


CVE-2020-1948远程代码执行漏洞原理是远程方法被动态调用导致的代码执行。从程序报错异常中分析中找到奔溃点,然后跟进到源码中可以发现atorg.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation.decode(DecodeableRpcInvocation.java:79)[dubbo-2.7.6.jar:2.7.6]开始分析,找到 readobject,再定位到com\alibaba\com\caucho\hessian\io\Hessian2Input.java,经过后续的多次循环,com\alibaba\com\caucho\hessian\io\ClassDeserializer.java与com\alibaba\com\caucho\hessian\io\ClassDeserial。得到是hessian问题造成漏洞原因。


补个漏洞不容易—Dubbo远程命令执行漏洞修复始末


基于此,Apache官方在2020年6月22日Apache官方发布了Dubbo 2.7.7版本,在此版本中,对invoke的method的进行了限制,在DecodeableRpcInvocation.java文件133-135行增加了对Method方法进行验证,如果验证不通过则抛出非法参数异常终止程序运行,用来修复此漏洞。


03

亡羊补牢



1.惨遭绕过


2020年7月1日,腾讯安全云鼎实验室就给出了Dubbo2.7.7反序列化漏洞绕过的方法。从官方发布的补丁对比文件来看,Dubbo在DecodeableRpcInvocation.java文件133-135行增加了对Method方法进行验证,如果验证不通过则抛出非法参数异常终止程序运行。通过对历史版本的回溯,发现在2019.10.31日的一次提交中DubboProtocol类的getInvoker函数的RemotingException代码块中增加了getInvocationWithoutData方法,对inv对象的arguments参数进行置空操作,用来缓解后反序列化攻击,此处正是CVE-2020-1948漏洞后反序列化利用的触发点。千里之堤溃于蚁穴,为了应对反序列化的漏洞而推出的Dubbo2.7.7,还是再一次走上了因为反序列化而引发远程命令执行的老路。

 其中MITRE Caldera执行的动作由计划系统结合预配置的ATT&CK模型生成。这样的好处在于能够更好更灵活地对攻击者的操作进行模拟,而不是遵循规定的工作序列。自动模拟攻击者进行攻击演练,安全地重现发生过的攻击行为,不会对资产造成损害,并且能够重复执行以对防御能力和检测能力进行测试和验证。


2.绕过原理


绕过选用的反射链是com.sun.rowset.JdbcRowSetImpl。此次dubbo的利用方式虽然也是在connect()方法中被使用jndi的方式加载恶意类,但是dubbo是动态调用了JdbcRowSetImpl的getDatabaseMetaData方法,造成了connect方法被执行。对提交修复的commit代码分析发现在DecodeableRpcInvocation中增加了输入参数类型的校验。点开查看代码,在原有的基础上增加了参数类型的校验,补丁代码如下图所示:


补个漏洞不容易—Dubbo远程命令执行漏洞修复始末


这里的parameterTypes是限制Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/Object;,如下图所示:


补个漏洞不容易—Dubbo远程命令执行漏洞修复始末


绕过2.7.7版本更新的传入的参数类型为Class<?>类型。类路径为Class<?>class com.rometools.rome.feed.impl.ToStringBean类,如果对参数检查是可以有效阻止漏洞的利用,然而修复错了地方。从git的commit记录来看,官方的修复思路推测是想在异常抛出前利用参数类型的校验并拦截,防止引用的toString()方法造成代码执行。但是拦截错了地方,通过对源码的单步调试发现,此处抛出的异常并不在漏洞触发的调用链上。

DecodeableRpcInvocation.java文件133-135行增加了对Method方法进行验证,如果验证不通过则抛出非法参数异常终止程序运行,核心代码代码如下:


补个漏洞不容易—Dubbo远程命令执行漏洞修复始末


跟进isGenericCall和isEcho方法,发现验证逻辑十分简单,如果method等于$invoke、$invokeAsync或者$echo则返回true。不得不说此处站在开发角度思考是没问题的,非Dubbo自带service中的$invoke、$invokeAsync、$echo方法以外,其他函数名全部抛出异常,但是万万没想到RPC调用过程中方法名是用户可控的,所以攻击者可轻易的将method设置为其中任意一个方法来绕过此处限制。


补个漏洞不容易—Dubbo远程命令执行漏洞修复始末


知道了method的验证逻辑,修改CVE-2020-1948 Poc中的的service_name和method_name参数的值,分别为:org.apache.dubbo.rpc.service.GenericService和$invoke。就可以验证通过,最后进入hession反序列化流程,成功执行了代码。


3.姗姗来迟


7月28日,ApacheDubbo终于发布了2.7.8的版本用来解决绕过问题。


补个漏洞不容易—Dubbo远程命令执行漏洞修复始末


时隔1个月发布的Dubbo2.7.8版本堵上了2.7.7解决反序列化的问题,在2.7.8版本发布的后的1个多月,还没有被爆出利用绕过的案例,至此,Dubbo的反序列漏洞的修补终于告一段落。


04

后记



梅须逊雪三分白,雪却输梅一段香。关于Dubbo反序列化漏洞的攻与防之争从来没有停止过,Dubbo如此,其他的服务框架亦如此。没有绝对的安全,只有动态的攻与防的平衡。作为安全从业者,我们要敬畏攻击,更要感谢攻击,他山之石,可以攻玉。以攻促防、以攻带防,了解对手、了解敌人,攻防一体、攻防兼备才可以推动企业的安全的新发展。


中国光大银行 信息科技部安全管理处
安小灰

END