Checkmarx研究:Apache Dubbo 2.7.3–通过不可信数据的反序列化实现未经认证的RCE(CVE-2019-17564)
近年来,我们对序列化攻击产生了浓厚的兴趣。就在几个月前,我们决定投入一点精力研究Apache Dubbo。据我们所知,Dubbo以多种方式对许多程序进行了反序列化处理。自Apache基金会使用Dubbo后,Dubbo在世界各地的用户数量显著增加。
图1 – Dubbo架构
据2019年中期新闻稿报道,中国有十几家企业已经开始使用Dubbo,其中包括阿里巴巴集团、中国人寿、中国电信、当当网、滴滴出行、海尔和中国工商银行等。该报道指出,Apache宣布将Dubbo提升为Apache顶级项目。
图2 – Dubbo用户(图片来源于Apache Dubbo官网)
我发现Apache Dubbo提供商和用户使用的版本≤Dubbo 2.7.3,当配置可接受HTTP协议时,允许远程攻击者向暴露的服务发送恶意程序,从而导致远程代码执行的问题。这是在未经认证的情况下发生的,攻击者只需知道很少的信息就可以利用此漏洞做文章。具体来说,如果HTTP启用,在任何Dubbo实例上,攻击者只需要利用此处所述的漏洞和一个URL就能顺利攻破服务系统。本报告附有概念验证视频。
攻击者可以利用此漏洞破解Dubbo提供商的服务(该服务旨在从其消费者获得远程连接)。然后攻击者可以用一个恶意的Dubbo提供商程序将现有Dubbo提供商程序替换掉。接下来,该替换程序可使用类似的恶意对象对消费者作出响应,从而激活远程代码执行程序,让攻击者攻破整个Dubbo集群。
造成这种漏洞问题的根本原因在于Spring框架中使用了远程反序列化服务。Spring框架文档明确建议,用户切勿使用不可信的数据运用Spring框架。再加上库(该库含一个鲜为人知的小工具链)过时的问题,导致远程代码执行的问题产生。不可信数据反序列化处理的不安全隐患,加上小工具链的存在,造成远程访问和远程未经认证的代码执行无缝对接的隐患产生。
Chris Frohoff和Moritz Bechler的研究和工具(ysoserial和marshalsec)功不可没。他们将一些代码用在小工具链上,他们的研究为解决这种漏洞问题奠定了基础。
严重级别
Checkmarx认为该漏洞的CVS评分为9.8(严重),因为它是一个未经认证的远程代码执行漏洞,在Dubbo服务的权限级别提供特权,从而完全破解了该服务的机密性、完整性和可及性。
虽然并非所有Dubbo实例都配置使用HTTP协议,但就配置使用该协议的Dubbo版本(带有已知漏洞)而言,只要提供很少且随时可用的信息(即存在漏洞的服务URL),这类系统就非常容易受到攻击。任何人只要通过注册表(如Zookeeper)等服务就可以在网上获得上述服务URL,且该服务URL不被视为秘密或机密内容。
CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H/E:F/RL:U/RC:C/CR:H/IR:H/AR:H
规范
这是怎么回事?
激活远程HTTP协议的Dubbo应用程序会产生反序列化的不安全问题。如果Apache Dubbo提供商实例启用了HTTP,攻击者就可以通过提交一个带有Java对象的POST请求,完全破解该实例。
Dubbo HTTP实例试图在Java ObjectStream中对数据进行反序列化处理,而Java ObjectStream包含一组恶意的类(常被称为小工具链),调用该工具链会导致系统执行恶意代码。在这种情况下,有问题的恶意代码允许攻击者对系统命令进行任意操作。异常创建期间,在这个小工具链上的Dubbo实例中进行内部toString调用时,小工具链就会被调用。
重建问题
攻击者可以将带有恶意对象的POST请求提交到Apache Dubbo HTTP服务的bean URL,以便执行远程代码。在这种情况下,bean是由Spring绑定到指定Dubbo协议端点的接口实现类。该bean连接到一个URL,该bean的请求体包含一个HTTP远程调用,用于确定适合的bean调用方法和参数的选用。
攻击者获得bean的URL之后,他们只需要通过标准POST请求提交一个恶意的小工具链即可利用这个漏洞做文章。
如果HTTP服务和协议已经启用,在具有Dubbo-Remoting-HTTP的vanilla Apache Dubbo范围内能够找到允许执行远程操作系统命令的新小工具链。
为概念验证(PoC)重建受损Dubbo HTTP实例
遵守以下指南:
- 遵守官方Apache Dubbo快速入门指南,直到成功创建能够正常运行的提供商程序和注册表
- 启用Dubbo HTTP服务——编辑dubbo-demo-provider.xml ——将Dubbo协议名称更改为“HTTP”
锁定易受攻击的实例
要触发此漏洞,攻击者必须识别一个指向Dubbo HTTP bean的URL。URL地址信息通常是公开或无需特权访问即可获得的,只要攻击者通过Zookeeper等Dubbo服务注册表以及多播就可以在网上获得该URL地址,无须部署良好的HTTPS传递途径,从而实现中间人攻击。
触发漏洞概念验证
有关运行的概念验证代码,参加附件1。请注意,Dubbo实例的IP地址等变量需要在这段代码中进行修改。
如上所述,攻击者需要与Dubbo HTTP服务相同的依赖项。在此概念验证中,com.nqzero:permit-reflect用于序列化期间所需的反射函数,而org.apache.httpcomponents.httpclient则被用于将恶意小工具发送到HTTP服务。为触发这个漏洞,攻击者使用Apache Dubbo和JDK的类空间中可用方法设计了新的小工具链。
此小工具链使用以下组件:
- remoting.httpinvoker.HttpinvokerServiceExporter——这是反序列化入口点,对请求体进行反序列化处理。HashMaps的反序列化和通常的Java集合调用其值的插入法。在这种情况下,将调用HashMap.putVal(h,k,v)。
- 两个springframework.aop.target.HotSwappableTargetSource对象的一个HashMap,一个包含作为目标的JSONObject,另一个包含作为目标的com.sun.org.apache.xpath.internal.objects.XString对象
- HotSwappableTargetSource对象总是返回相同的hashcode (class.hashCode()),这迫使putVal(h,k,v)对HashMap键运行更深层次的相等性检查,在其内容上触发equals()——两个HotSwappableTargetSource成员对象
- HotSwappableTargetSource相等性检查验证HotSwappableTargetSource内的目标对象是否相等;在这种情况下:一个XString和一个JSONObject
- equals(object)会触发一个与this.toString().equals(object.toString())对等的调用,触发JSONObject.toString()
- JSONObject – org.apache.dubbo.common.json.JSONObject是Dubbo中的一个不推荐使用的类,用于处理JSON数据。如果调用toString(),超级方法JSON.toJSONString()将被调用。
- 一个toJSONString()调用将尝试使用JSONSerializer,调用序列化程序将对象序列化为JSON。此序列化程序是使用ASMSerializerFactory生成的。该工厂试图序列化存储在JSONObject中的对象中的所有getter法。
- TemplatesImpl partial gadget ——这种已知的小工具被ysoserial和marshalsec中的许多小工具链使用。这个小工具会生成恶意的sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl对象。如果调用该对象的newTransformer()方法,该链将执行java.lang.Runtime.getRuntime().exec(command)
- 由于toJSONString试图序列化所有的getter方法,所以TemplatesImpl.getOutputProperties()也被调用了
- 在内部,getOutputProperties()方法调用newTransformer(),从生成的转换器获取属性
图3–利用字节码
如果调用了newTransformer,
HttpInvokerServiceExporter.doReadRemoteInvocation(ObjectInputStream ois)
和java.lang.Runtime.getRuntime().exec(command)之间的反序列化流程就完成了,从而实现远程代码执行。
最终小工具链的结构如下:
如果该漏洞被触发,并且恶意代码被执行(在概念验证中,服务器上会弹出一个calc.exe实例),就会引发异常。然而,应用程序将继续按照预期的方式运行,从而稳定地利用给定的小工具链。
图4–概念验证结果
为什么会发生这种情况?
当在Spring框架上使用HTTP协议创建Dubbo应用程序时,就会出现使用HTTP远程处理的Apache Dubbo。Dubbo毫无防备地在Spring中调用一个已知存在漏洞的类,并使用极易受攻击(几乎没有防范能力)的ObjectInputStream反序列化用户输入。攻击者可以提供一个有效负载,当反序列化时,该有效负载将触发一连串的对象和方法调用,如果在反序列化范围内给定一个易受攻击的小工具链,可能会导致远程代码执行,这将在本概念验证中演示。
易受攻击的Spring Remoting类是HttpInvokerServiceExporter。来自Spring文档中:
“警告:请当心不安全的Java反序列化导致的漏洞问题:在反序列化步骤中,被操纵的输入流会导致服务器上不需要的代码执行。因此,不要将HTTP调用方端点暴露给不可信的客户端,而是只在您自己的服务之间公开。一般来说,我们强烈推荐采用任何其他消息格式替代(如JSON)。”
这正是Dubbo HTTP远程处理所发生的情况。使用Dubbo HTTP远程处理模块,公开一个接收以下结构的HTTP请求的HTTP端点:
- POST请求
- 其URL是指提供商公开的bean的classname,它由Dubbo连接到实际的程序包和类
- 其主体是对象的流,由ObjectOutputStream序列化
HttpProtocol处理程序使用HttpInvokerServiceExporter解析传入的org.apache.dubbo.rpc.protocol.http.HttpRemoteInvocation对象,该对象在内部利用ObjectInputStream对其进行反序列化。HttpRemoteInvocation包含对某个方法的调用,以及该方法的传递参数。但是,使用ObjectInputStream,可以传递任何任意序列化的Java对象,然后以不安全的方式反序列化这些对象,从而导致不安全的反序列化。
ObjectInputStream本身没有任何外部类,当它用于反序列化格式错误的嵌套对象时,容易受到内存耗尽和堆溢出攻击。
如果允许代码或命令执行的代码范围内有ObjectInputStream可反序列化小工具链,则攻击者可以利用这一点来创建导致远程代码执行的对象。这样一个小工具链被发现和利用。
受污染的代码流
在Dubbo HTTP服务中,会发生以下情况:
- 通过用户输入调用JavaX HttpServlet
- 此输入被传递给Dubbo远程调度程序DispatcherServlet,该程序使用HttpProtocol中的内部类HttpHandler来处理请求并返回响应
- handle()在第210行创建不安全的HttpInvokerServiceExporter,并在第216行的请求中调用它
- 从那里,HttpInvokerServiceExporter中的内部调用最终将请求流传递到第115行的ObjectInputStream中,然后由第144行的处理程序的超类RemoteInvocationSerializingExporter在内部读取。
- 然后ObjectInputStream readObject操作触发小工具链
开发所需的先验知识
利用开放的HTTP端口访问Dubbo HTTP Remoting服务所需知道的唯一内容就是远程调用(Remote Invocation)接口的程序包和类的名称。该信息用于创建URL,后者是序列化恶意对象必须提交的地方,这是标准Spring bean行为。举例而言,如果远程接口的程序包名称为“org.apache.dubbo.demo”,而被远程处理的接口名称为“DemoService”,则攻击者需要将由ObjectOutputStream序列化的对象发布到URL“http://domain:port/org.apache.dubbo.demo.DemoService”上。这些URL信息可以以下各种方法获得:
- 如果Dubbo使用Zookeeper作为注册表,则用Zookeeper查询可用的beans
- 通过中间人攻击观察HTTP流量
- 如果Dubbo使用多播查找服务,欺骗行为也是可能发生的(这未经测试)
- 如日志服务等其他方式
执行攻击不需要额外的信息。
应该注意的是,人们一般不会将URL路径视为机密信息,但网络攻击者可以在所谓的不可知的URL路径后面隐藏易受攻击的网络服务,通过隐蔽性的操作,对服务安全构成威胁。
时间轴和披露信息总结
Checkmarx研究团队首次发现这个漏洞后,致力于找到能够再现漏洞攻击进程的方法。一旦证实攻击者的漏洞攻击方法,研究团队就会负责任地将自己的研究发现告知Apache。
披露时间轴
- 2019年8月13日——Checkmarx向security@apache.org提供完整的披露信息,将漏洞问题转发给了security@dubbo.apache.org
- 2019年9月6日——Apache,Dubbo团队确认找到了问题
- 2019年10月4日——Dubbo团队对拟定解决的技术问题做出回应。Checkmarx通过进一步阐述问题,做出回应。在本次披露时间的背景下,这是Apache员工第一次提出技术问题,也是最后一次
- 2019年11月24日——Apache一方未能在90天后发出提醒,且没有发表任何言论
- 2019年12月3日——在Checkmarx发布报告前,Apache申请了更多的时间,用于对这个问题进行重新评估。这一请求被批准,两天后Apache确认将发布CVE,并公布适合的解决方案
- 2020年2月11日——CVE-2019-17564通过dev@dubbo.apache.org邮件列表进行披露,披露时间是在首次披露后的六个月(180天)
- 2020年2月12日——第一个POC在自然环境下出现,但它不包含本文中公开的新小工具链
程序包版本
org.apache.dubbo.dubbo – 2.7.3
org.apache.dubbo.dubbo-remoting-http – 2.7.3
org.springframework.spring-web – 5.1.9.RELEASE
供应商防范
Apache Dubbo团队通过将FastJSON(含最新版本JSONObject)更新为项目依赖项中的最新版本,解决了这个问题,有效地打破了当前的枷锁。他们还将HTTP协议使用的反序列化机制进行替换,变更了通信协议,确保这种特定的攻击无法发挥作用。
结论
Dubbo HTTP Remoting服务容易受到未经认证的远程代码执行的攻击。事实上,攻击者除了需要知道URL的信息,不需要知道任何先验知识就可以顺利通过该漏洞对该服务进行攻击。
造成这种漏洞问题的根本原因是系统使用了一个不安全的Spring类,HttpInvokerServiceExporter,用于绑定HTTP服务。该类使用标准的Java ObjectStream,并且没有白名单类形式的安全机制,却能让反序列化允许调用其反序列化过程可能触发恶意代码的任意类。这时应该停止使用这个类,采用完善的解决方案替代,将DubboHTTP beans中的预期类列入白名单。
Checkmarx安全研究团队持续开展这类研究活动旨在让全球各地的企业都能在软件安全实践方面进行必要的变革,以努力提高所有人的整体安全防护级别。了解更多安全研究可以关注Checkmarx微信公众号和官网。
附件1
附件1A:DubboGadget类
一个用于攻击Dubbo HTTP实例的类
附件1B: Utils类
实用类,包括实用方法,用于创建恶意小工具链的某些部分,并通过简化反射来公开某些函数。很大程度上,实用类源自Chris Frohoff系列文章中的辅助类和舒适方法——https://github.com/frohoff/ysoserial。此外,makeXStringToStringTrigger源自Moritz Bechler的前期研究,示例参见https://github.com/mbechler/marshalsec
附件1C:用于DubboGadget的pom.xml文件
DubboGadget的Maven依赖项
相关文章
- 1条评论
- 世味十雾2022-06-05 16:47:54
- 少且随时可用的信息(即存在漏洞的服务URL),这类系统就非常容易受到攻击。任何人只要通过注册表(如Zookeeper)等服务就可以在网上获得上述服务URL,且该服务URL不被视为秘密或机密内容。CVSS:3