您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關如何實現Apache Ofbiz 反序列化漏洞CVE-2020-9496分析,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
2020年09月29日, 360CERT對Apache ofbiz
組件的反序列化漏洞進行了分析,該漏洞編號為 CVE-2020-9496
,漏洞等級:高危
,漏洞評分:8.0
。
Apache ofbiz
存在 反序列化漏洞
,攻擊者
通過 訪問未授權接口,構造特定的xmlrpc http請求
,可以造成 遠程代碼執行的影響
。
360CERT對該漏洞的評定結果如下
評定方式 | 等級 |
---|---|
威脅等級 | 高危 |
影響面 | 一般 |
360CERT評分 | 8.0分 |
- Apache Ofbiz:< 17.12.04
XML-RPC
是一種遠程過程調用(RPC
)協議,它使用XML
對其調用進行編碼,并使用HTTP
作為傳輸機制。它是一種規范和一組實現,允許軟件運行在不同的操作系統上,運行在不同的環境中,通過Internet
進行過程調用。在XML-RPC
中,客戶端通過向實現XML-RPC
并接收HTTP
響應的服務器發送HTTP
請求來執行RPC
。
客戶端
package org.apache.xmlrpc.demo.client;
import java.net.URL;
import org.apache.xmlrpc.client.XmlRpcClient;
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
import org.apache.xmlrpc.client.XmlRpcSunHttpTransportFactory;
public class Client {
public static void main(String[] args) throws Exception {
// 創建客戶端實例
XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
config.setServerURL(new URL("http://127.0.0.1:8081/xmlrpc"));
XmlRpcClient client = new XmlRpcClient();
client.setConfig(config);
// 設置傳輸工廠類
client.setTransportFactory(new XmlRpcSunHttpTransportFactory(client));
// 創建遠程方法的參數數組,通過指定遠程方法名稱進行調用
Object[] params = new Object[]{new Integer(33), new Integer(9)};
Integer result = (Integer) client.execute("Calculator.add", params);
System.out.println(result);
}
}
或者客戶端使用動態代理的方式,通過ClientFactory
,需要客戶端和服務端都要有Adder
的接口,具體實現類在服務端。
但要使用XML-RPC
的動態代理功能,相應的服務器端的處理器類名稱必須是Client
端接口類的全名(含包名,該名稱一般應該與Server
端接口類全名一致),否則將會導致調用失敗。
public class Client_Proxy {
public static void main(String[] args) throws MalformedURLException {
XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
config.setServerURL(new URL("http://127.0.0.1:8081/xmlrpc"));
XmlRpcClient client = new XmlRpcClient();
client.setConfig(config);
ClientFactory factory = new ClientFactory(client);
Adder adder = (Adder) factory.newInstance(Adder.class);
int sum = adder.add(2, 4);
System.out.println(sum);
}
}
Adder接口
package org.apache.xmlrpc.demo.proxy;
public interface Adder {
public int add(int pNum1, int pNum2);
}
服務端
package org.apache.xmlrpc.demo.webserver;
import org.apache.xmlrpc.server.PropertyHandlerMapping;
import org.apache.xmlrpc.server.XmlRpcServer;
import org.apache.xmlrpc.server.XmlRpcServerConfigImpl;
import org.apache.xmlrpc.webserver.WebServer;
public class Server {
private static final int port = 8081;
public static void main(String[] args) throws Exception {
WebServer webServer = new WebServer(port);
XmlRpcServer xmlRpcServer = webServer.getXmlRpcServer();
PropertyHandlerMapping phm = new PropertyHandlerMapping();
/* Load handler definitions from a property file.
* The property file might look like:
* Calculator=org.apache.xmlrpc.demo.Calculator
* org.apache.xmlrpc.demo.proxy.Adder=org.apache.xmlrpc.demo.proxy.AdderImpl
*/
phm.load(Thread.currentThread().getContextClassLoader(),
"MyHandlers.properties");
/* You may also provide the handler classes directly,
* like this:
* phm.addHandler("Calculator",
* org.apache.xmlrpc.demo.Calculator.class);
* phm.addHandler(org.apache.xmlrpc.demo.proxy.Adder.class.getName(),
* org.apache.xmlrpc.demo.proxy.AdderImpl.class);
*/
phm.addHandler("Calculator",
org.apache.xmlrpc.demo.Calculator.class);
xmlRpcServer.setHandlerMapping(phm);
XmlRpcServerConfigImpl serverConfig =
(XmlRpcServerConfigImpl) xmlRpcServer.getConfig();
serverConfig.setEnabledForExtensions(true);
serverConfig.setContentLengthOptional(false);
webServer.start();
}
}
服務端調用類
package org.apache.xmlrpc.demo;
public class Calculator {
public int add(int i1, int i2) {
return i1 + i2;
}
public int subtract(int i1, int i2) {
return i1 - i2;
}
}
在服務端還需要創建一個MyHandlers.properties
。
啟動服務端之后,運行客戶端。
抓取流量。動態代理和普通的流量都是一樣的。
客戶端向/xmlrpc
發了一個POST
請求,請求的內容為:
<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>Calculator.add</methodName>
<params><param>
<value>
<i4>33</i4>
</value>
</param><param>
<value>
<i4>9</i4>
</value>
</param></params>
</methodCall>
每個XML-RPC
請求都以XML
元素<methodCall> </methodCall>
開頭。該元素包含單個子元素<methodName>xxx</methodName>
。元素<methodName>
包含子元素<params>
,該子元素可以包含一個或多個<param>
元素。 param XML
元素可以包含許多數據類型。
服務端響應的內容為
<?xml version="1.0" encoding="UTF-8"?>
<methodResponse xmlns:ex="http://ws.apache.org/xmlrpc/namespaces/extensions">
<params><param>
<value>
<i4>42</i4>
</value>
</param></params>
</methodResponse>
注:`ofbiz 17.12.03`版本的`commons-beanutils`依賴版本是`1.9.3`
在第一次加載ControlServlet
的時候,會調用init
進行初始化,此時會通過getRequestHandler
來初始化RequestHandler
。
調用getControllerConfigURL
方法,
該方法從配置文件/WEB-INF/controller.xml
中獲取定義好的請求映射的列表,這里會遍歷所有webapp
的controller.xml
配置文件。
其中,定義xmlrpc
的配置,沒有設置對應的auth
選項,默認為false
,不需要身份驗證,這也是之后修復的一個點。
<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>
官方文檔給出的配置文件的tag
:
然后實例化ViewFactory
和EventFactory
。
先看實例化ViewFactory
,會根據配置文件里的type
是view
屬性中對應的值,遍歷出所需要的Viewerhandler
,并且進行init
初始化。然后存入map
。
webtools
下的配置文件就存在9
中type
。
接著實例化EventFactory
,同樣遍歷出type
是event
的EventHandler
,并進行init
初始化,然后存入map
。
然后把所有初始化的設置到servletContext
里。
根據公開的zdi
文章,xml
的執行是在webtools/control/xmlrpc
,于是去/webtools/webapp/webtools/WEB-INF/web.xml
中查看相關路由的處理。
<servlet-mapping>
<servlet-name>ControlServlet</servlet-name>
<url-pattern>/control/*</url-pattern>
</servlet-mapping>
請求/control
下的資源都由ControlServlet
來進行處理,是所有請求處理的核心。post
請求也會由ControlServlet#doGet
方法進行處理:
首先,調用getRequestHandler
,因為已經初始化了,所以能夠直接獲取到。
接著往下走,是處理request
請求的一些東西,然后調用requestHandler.doRequest
方法 根據request
請求獲取當前請求的appname
,這里是webtools
,然后獲取pathinfo
也就是xmlrpc
。
然后根據pathinfo
從config
里獲取對應的配置,繼續往下走,requestMap.event
也就是之前根據配置所獲取到的xmlrpc
。
type
,path
,invoke
都不為null
,于是跟進runEvent
。根據type
從eventFactory
中獲取eventhandler
,
然后調用eventHandler.invoke
,這里我們的echo
參數為null
,于是調用execute
方法。
跟入getRequest
方法。
前幾行是在為SAX
解析做準備,設置了一個handler
為XmlRpcRequestParser
,結構如下:
主要的element
解析發生在XmlRpcRequestParser
里。
然后調用parse
函數,正式進入http xml
解析的流程。
這里xml
的解析主要采用SAX
方式解析。SAX
解析觸發的事件有
startDocument:開始讀取XML文檔;
startElement:讀取到了一個元素,例如<book>;
characters:讀取到了字符;
endElement:讀取到了一個結束的元素,例如</book>;
endDocument:讀取XML文檔結束。
整個scan
流程主要發生在XMLDocumentFragmentScannerImpl#dispatch
,之前的調用棧如下:
用fScannerState
用來標識當前該調用哪個方法來解析tag
。
剛開始解析的時候,state
為6
:
調用scanRootElementHook
,用于掃描根元素,Resolver
為null
,于是進入else
,
接著在AbstractSAXParser#startElement
,會調用ContentHandler.startElement
,這個content
是之前初始化的時候設置的,也就是XmlRpcRequestParser#startElement
方法。
在XmlRpcRequestParser#startElement
函數里,支持解析methodCall
,methodName
,params
,parma
,value
標簽,如果不是,那么交給父類RecursiveTypeParserImpl
做進一步處理。
判斷到methodName
會把inMethodName
設置為true
,之后,在dispatch
根據state
進入分支處理content
,然后會調用XmlRpcRequestParser#characters
,設置methodName
屬性。
下一個標簽是結束標簽</methodName>
調用endElement
,將inMethodName
設置為false
。
如果解析到是value
里的子標簽,那么會調用startValueTag
方法。
會設置inValueTag
屬性為true
。
后續,比如在解析serializable
標簽的時候,就會進入default
分支。
RecursiveTypeParserImpl#startElement
,剛開始typeParser
為null
。
在getParser
方法里會根據標簽從TypeFactoryImpl
里去創建具體對應的Parser
,并且,想要拿到第一個判斷里的擴展Parser
,還需要指定pURI
。
我們poc
里的value
里的xml
結構為:
<struct>
<member>
<name>test</name>
<value>
<serializable>
{base64codehere}
</serializable>
</value>
</member>
</struct>
于是首先實例化的是MapParser
,于是XmlRpcRequestParser
的typeParser
為MapParser
.
還是先調用Parser.startDocument
,
接著調用其對應的startElement
方法。
判斷struct
標簽后,然后解析下一個標簽member
重復之前的過程,XmlRpcRequestParser
解析不了,交給RecursiveTypeParserImpl
處理。但是此時typeParser
已經不為null
,于是直接調用MapParser#startElement
進行處理。
中間其他tag
省略了,注意serializable
標簽之前還有一個value
標簽,但是不在XmlRpcRequestParser
處理,因為此時level
已經很大了,直接看到MapParser
對value
的處理
調用startValueTag
,重新將typeParser
設置為null
,但是這里設置的是MapParser
的typeParser
,這一步很關鍵,typeParser
為null
才能重新獲取parser
。
在解析serializable
的時候,XmlRpcRequestParser
的typeParser
依然是MapParser
,但是在MapParser
里處理不了serializable
標簽,此時再交給RecursiveTypeParserImpl
,這時獲取到的就是MapParser
的typeParser
,因為之前被設置為null
,所以重新獲取Parser
,而這時解析到serializable
標簽,于是getParser
返回為SerializableParser
。
SerializableParser
繼承ByteArrayParser
,沒有startElement
方法,于是調用父類ByteArrayParser
,設置OutputStream
,且解碼輸入流。
接著處理</serializable>
在Serializable#endElement
,setResult
給result
賦值。
接著是處理</value>
,在MapParser#endElement
。
跟入endValueTag
,typeParser
為Serializable
。
調用getResult
,取出result
并造成反序列化。
Fixed: Apache OFBiz unsafe deserialization of XMLRPC arguments
https://github.com/apache/ofbiz-framework/commit/4bdfb54ffb6e05215dd826ca2902c3e31420287a#diff-b31806fbf9690361ad449e8f263345d8
直接在controller.xml
配置xmlrpc
需要授權訪問。
xmlrpc
本身是支持對序列化數據的反序列化的,而問題就出現在ofbiz
沒有對xmlrpc
接口的訪問做權限的控制,同時版本較低的情況下又存在能夠被反序列化所利用的依賴。
以上就是如何實現Apache Ofbiz 反序列化漏洞CVE-2020-9496分析,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。