Websphere CVE-2020-4450漏洞分析


Websphere CVE-2020-4450漏洞分析

 
原創 觀星實驗室 iswin

來自於公眾號: 奇安信安全服務 
原文鏈接: https://mp.weixin.qq.com/s?__biz=MzI4MzA0ODUwNw==&mid=2247485139&idx=1&sn=8a57a5382cee3d5e182d7f1e443adf7f&chksm=eb91e987dce660916e874fe63f83772cc122fbe0abcfe5a7e69b09e4c182048c4705ec6d5a81&mpshare=1&scene=23&srcid=0731JzAq0tPf4F5aCINjzXPE&sharer_sharetime=1596190153031&sharer_shareid=ff83fe2fe7db7fcd8a1fcbc183d841c4#rd

漏洞簡介
7月20號,ZDI官方Blog公布了一個名為abusing-java-remote-protocols-in-ibm-websphere ( https://www.thezdi.com/blog/2020 ... ls-in-ibm-websphere)的文章,文章中提到了Websphere的兩個漏洞,一個是RCE,另外一個是XXE,根據Blog中的內容來看,漏洞屬於IIOP協議的反序列化,也是基於JNDI的利用,但是跟常規的JNDI利用有一些不同之處,一方面是Websphere將IIOP替換成自己的實現,另外就是Websphere嚴格的類加載機制導致大部分公開的利用鏈都沒法利用,這個漏洞利用需要訪問至少2809端口以及兩次外連請求,從紅隊利用角度來看稍微有點雞肋,但是漏洞的利用思路以及EXP的構造都非常的有意思,是一個值得研究的漏洞。

漏洞環境准備

經常做分析的同學都有深刻體會,針對有些不了解、不熟悉的系統進行分析時往往在環境准備上會耗費大量時間,而且有部分漏洞的利用需要在特定的條件和環境下進行,經常是“環境准備1天,分析調試10分鍾”。

Websphere的安裝有兩種方式:

1、在線安裝,直接在 https://www.ibm.com/support/page ... -download-documents下載Installation Manager工具就可以在線安裝,國內的網速環境比較慢,需要掛代理然后多刷新幾次,直到出來以下界面,說明就OK了。



2、離線安裝,這種方式現在官方基本上不推薦,而且離線的安裝包基本上都是8.X的相對來說比較老,離線安裝基本上需要將低包和安裝程序都全部下載下來。

注:盡量不要選擇基於Docker的安裝環境,JAVA大部分RMI通信會依賴其他端口(一般是高端口)進行通信,安裝的時候一定不要選補丁,不然復現不了,在線安裝的版本是自帶補丁的版本,本次測試的環境主要覆蓋了兩個版本,8.5.5.0以及9.0.0.2版本,這兩個版本基本上涵蓋了主流的版本。

漏洞分析

一般情況下調試之前我們要看下端口對應是哪些進程啟動,然后給對用的進程加上遠程Remote Debug選項,Websphere的遠程調試直接在后台對應Application Server下面設置Remote Debug就可(2809的端口以及其它幾個端口PID都一樣),Websphere之前沒有針對性的看過,根據官方的描述我們直接將斷點打在com.ibm.ws.Transaction.JTS.TxServerInterceptor#receive_request上,然后用IIOP客戶端直接連接,觸發斷點之后,就可以在堆棧里面看到完整請求的觸發路徑。


這個回溯的時候我們可以看一下這個漏洞觸發點的位置,由於這個點是未授權,所以我們通過回溯可以看一下整體的流程,這個點的Interceptor就類似Java WEB中Filter的功能,回溯到com.ibm.rmi.pi.InterceptorManager#iterateServerInterceptors,可以看到還有一些其他的攔截器。


這些點都可以有助於分析人員對系統框架設計上的一些了解,下來直接看到關鍵的觸發點,我們需要首先解決的問題是如何能到達漏洞觸發點。


我們的目標是進入TxInterceptorHelper.demarshalContext方法,那么這里核心的點就是保證ServiceContext serviceContext = ((ExtendedServerRequestInfo)sri).getRequestServiceContext(0);代碼片段中serviceContext不為空,同時serviceContext.context_data的內容不為空,所以我們先解決如何構造數據包的問題。

IIOP協議的鏈接主要有兩種方式,一種是JAVA提供的標准客戶端連接方式,這個的好處是可以通過設置java.naming.factory.initial對應的實現類去處理多種協議,例如T3(S)/IIOP(S)/LDAP(S)等等。
​​​​​​​

Properties env = new Properties();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.ibm.websphere.naming.WsnInitialContextFactory");
env.put(Context.PROVIDER_URL, "iiop://192.168.18.130:2809");
InitialContext initialContext = new InitialContext(env);
initialContext.list("sglab");


另外一種就是用ORB客戶端直接連接,這種相對來說比較直接一點:​​​​​​​
Properties props = new Properties();
props.put("org.omg.CORBA.ORBInitialPort", "2809");
props.put("org.omg.CORBA.ORBInitialHost", "192.168.18.130");
ORB orb = ORB.init(args, props);
org.omg.CORBA.Object orbref = orb.resolve_initial_references("NameService");

現在的問題是如何在連接過程中設置相應的ServiceContext內容,經過一番搜索,發現可以通過第一種連接方式然后反射手動去加一個ServiceContext的實例,這里直接給出相應的實現,具體怎么找還是去Debug看變量。


現在可以到漏洞觸發點了:


下面主要是看如何進行數據包的構造,為了能觸發反序列化,程序必須得執行到如下代碼片段第80行,propContext.implementation_specific_data = inputStream.read_any(); 代碼片段。


由於前面有一堆的read*操作在demarshalContext函數中,那么我們可以看下對應marshalContext函數是怎么把對象序列化並且包裝發出去的:​​​​​​​
public static final byte[] marshalContext(PropagationContext propContext, ORB orb) {
       if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled()) {
           Tr.entry(tc, "marshalContext");
       }

       byte[] result = null;
       CDROutputStream outputStream = ORB.createCDROutputStream(orb);
       outputStream.putEndian();
       PropagationContextHelper.write(outputStream, propContext);
       byte[] result = outputStream.toByteArray();
       outputStream.releaseBuffer();
       if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled()) {
           Tr.exit(tc, "marshalContext", result);
       }

       return result;
   }

到這里就可以照貓畫虎生成payload,具體的代碼片段如下:


這里WSIFPort_EJB對象在反序列化的時候回去調用一個對象fieldEjbObject,這個對象的構造稍微麻煩點,我是直接重寫了com.ibm.ejs.container.EJSWrapper#getHandle函數,這樣所有的構造都有可以在這里面進行,后面會講到如何進行構造,到目前我們可以進行到反序列化的點,整體的調用鏈如下(主要是前面提到的inputStream.read_any()到最后調用的函數)

readObject:513, WSIFPort_EJB (org.apache.wsif.providers.ejb)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:90, NativeMethodAccessorImpl (sun.reflect)
invoke:55, DelegatingMethodAccessorImpl (sun.reflect)
invoke:508, Method (java.lang.reflect)
invokeObjectReader:2483, IIOPInputStream (com.ibm.rmi.io)
inputObjectUsingClassDesc:2010, IIOPInputStream (com.ibm.rmi.io)
continueSimpleReadObject:749, IIOPInputStream (com.ibm.rmi.io)
simpleReadObjectLoop:720, IIOPInputStream (com.ibm.rmi.io)
simpleReadObject:669, IIOPInputStream (com.ibm.rmi.io)
readValue:193, ValueHandlerImpl (com.ibm.rmi.io)
read_value:787, CDRReader (com.ibm.rmi.iiop)
read_value:847, EncoderInputStream (com.ibm.rmi.iiop)
unmarshalIn:273, TCUtility (com.ibm.rmi.corba)
read_value:664, AnyImpl (com.ibm.rmi.corba)
read_any:467, CDRReader (com.ibm.rmi.iiop)
read_any:797, EncoderInputStream (com.ibm.rmi.iiop)
demarshalContext:171, TxInterceptorHelper (com.ibm.ws.Transaction.JTS)
receive_request:180, TxServerInterceptor (com.ibm.ws.Transaction.JTS)
invokeInterceptor:608, InterceptorManager (com.ibm.rmi.pi)
iterateServerInterceptors:521, InterceptorManager (com.ibm.rmi.pi)
iterateReceiveRequest:732, InterceptorManager (com.ibm.rmi.pi)
dispatchInvokeHandler:629, ServerDelegate (com.ibm.CORBA.iiop)
dispatch:508, ServerDelegate (com.ibm.CORBA.iiop)
process:613, ORB (com.ibm.rmi.iiop)
process:1584, ORB (com.ibm.CORBA.iiop)
doRequestWork:3210, Connection (com.ibm.rmi.iiop)
doWork:3071, Connection (com.ibm.rmi.iiop)
doWork:64, WorkUnitImpl (com.ibm.rmi.iiop)
run:118, PooledThread (com.ibm.ejs.oa.pool)
run:1892, ThreadPool$Worker (com.ibm.ws.util)

這里解決了觸發反序列化的這個過程,由於IBM Wesphere自定義的Classloader干掉了一些利用鏈中所需要類,導致公開的利用鏈是打不死的,所以我們需要基於WSIFPort_EJB 這個類去找一個新的利用鏈,基於WSIFPort_EJB 最終利用點在com.ibm.ejs.container.EntityHandle#getEJBObject中的下面代碼。


92行是漏洞最終的觸發點,根據這段代碼我們可以看到,ctx.lookup這個函數必須是實現EJBHOME接口的類,這樣才能到100行中最后去觸發利用點,假定我們不繼續往后面跟進,到這里我們可以找到homeClass的要求,要實現或者EJBHOME接口,同時有聲明findFindByPrimaryKey方法,並且參數是Serializable類型,那么我們可以快速找到一個接口com.ibm.ws.batch.CounterHome,該接口的具體定義如下:
​​​​​​​

public interface CounterHome extends EJBHome {
   Counter create(String var1) throws CreateException, RemoteException;

   Counter findByPrimaryKey(String var1) throws FinderException, RemoteException;
}


完全滿足我們的需求,現在就是要去找ctx.lookup返回結果滿足上面的要求的類,根據ZDI文章中的內容,這個IIOP的實現完全被IBM自己實現了一遍,所以傳統的直接去LDAP或者RMI利用在這里是肯定不行的,這個地方的JNDI的調用邏輯主要如下:
com.sun.jndi.rmi.registry.RegistryContext#lookup 
com.sun.jndi.rmi.registry.RegistryContext#decodeObject 
javax.naming.spi.NamingManager#getObjectInstance 
org.apache.aries.jndi.OSGiObjectFactoryBuilder#getObjectInstance 
org.apache.aries.jndi.ObjectFactoryHelper#getObjectInstance 
org.apache.aries.jndi.ObjectFactoryHelper#getObjectInstanceViaContextDotObjectFactories(其他函數路徑也可以)

我們直接跟進進行進一步分析,重點看org.apache.aries.jndi.ObjectFactoryHelper#getObjectInstance這個函數,JNDI的利用包括RMI的那個BYPASS基本上都在找javax.naming.spi.ObjectFactory接口新的實現類:


這里可以看到我們可以指定String factories = (String)environment.get("java.naming.factory.object");這個變量,environment變量是我們在反序列化的時候控制的內容,所以這里需要去找一個ObjectFactory可以利用的實現類,可以找到ZDI文章中說的這個org.apache.wsif.naming.WSIFServiceObjectFactory這個類,這個類我們可以看到:


到這里不由得讓我們想起了之前那個RMIBYPASS的場景 https://www.veracode.com/blog/re ... ndi-injections-java ,倒着我們就可以自定義一個RMI服務,將bind的內容設置成我們可以控制的org.apache.wsif.naming.WSIFServiceStubRef類型,就可以到最下面的函數,為什么上面那個org.apache.wsif.naming.WSIFServiceRef不行,因為前面提到了這個類必須要是EJBHOME的實現類,上面的明顯不符合要求,下面的是通過動態代理來生成一個指定接口的類,而且接口的類型我們也可以控制,所以接下來就是如何利用wsif服務來進行代碼執行了。

這里ctx.lookup里面的jndi的地址可以設置為自定義rmi的服務,具體的RMI服務代碼如下:


這里就可以控制wsif相關的內容以及className接口的名稱這里可以在設置Reference ref = new Reference(WSIFServiceStubRef.class.getName(), (String)null, (String)null);來指定具體的實現,配合org.apache.wsif.providers.ejb.WSIFPort_EJB序列化中java.naming.factory.object的類型來指定factory的實現類,也可以直接在這里指定factory的實現類這樣客戶端就不需要指定,兩種方式都可以。

WSIFPort_EJB中fieldEjbObject對象的生成內容如下(我手動覆蓋了com.ibm.ejs.container.EJSWrapper#getHandle)函數


關於WSIF服務如何進行RCE,這里說下關鍵點,具體的Sample參考 https://www.ibm.com/support/know ... ae/twsf_devwes.html,看完弄個EXP肯定是沒問題,factory返回的對象是一個動態代理,實現了com.ibm.ws.batch.CounterHome接口,最終在調用findByPrimaryKey函數的時候回去調用org.apache.wsif.base.WSIFClientProxy#invoke方法,這個方法中使用了WSIFOperation wsifOperation = this.wsifport.createOperation(method.getName(), inputName, outputName); 函數去請求wsif服務進行遠程方法調用。

WSIF服務的wsdl描述文件中提供了JavaBind的方式,可以將描述文件中定義的函數(operation name)映射成客戶機器中Java類的函數,所以這里我們可以將在wsif描述文件中定義findByPrimaryKey函數以及映射函數的參數和返回類型,這樣在最終調用findByPrimaryKey函數的時候會調用到org.apache.wsif.base.WSIFClientProxy#invoke中createOperation函數去進行遠程方法調用,客戶端在拿到映射后就可以去執行映射類相應的函數了。

這里映射的類和函數是javax.el.ELProcessor類的eval方法,eval函數接受一個String類型的參數映射是符合相應的定義,WSIF對應的XML文件關鍵片段如下:

<binding name="JavaBinding" type="tns:RceServicePT">
<java:binding/>
<format:typeMapping encoding="Java" style="Java">
   <format:typeMap typeName="xsd:string" formatType="java.lang.String"/>
   <format:typeMap typeName="xsd:object" formatType="java.lang.Object"/>

</format:typeMapping>

<operation name="findByPrimaryKey">
   <java:operation
                   methodName="eval"
                   parameterOrder="expression"
                   methodType="instance"
                   returnPart="result"/>
   <input name="getExpressionRequest"/>
   <output name="getExpressionResponse"/>
</operation>
</binding>

<service name="rce_service">
<port name="JavaPort" binding="tns:JavaBinding">
   <java:address className="javax.el.ELProcessor"/>
</port>
</service>


而且findByPrimaryKey的參數也是我們在WSIFPort_EJB序列化數據中可以控制的,所以最終就導致了RCE。

漏洞利用

最開始在測試Websphere 8.5.5.0的時候,發現有個關鍵位置:


這里獲取到Proxy的代理對象result的時候默認就調用ObjectFactoryHelper.logger.log(Level.FINE, "result = " + result);方法,這個函數會導致調用Proxy代理對象的toString方法,間接的調用org.apache.wsif.base.WSIFClientProxy#invoke方法,org.apache.wsif.base.WSIFClientProxy#invoke方法中有個非常關鍵的函數(this.findMatchingOperation(method, args);)會導致EXP利用中斷,如下圖:


這里要求調用的函數必須是繼承接口(EJBHOME)中一個接口,否則程序主動拋異常(Exception in thread "main" java.lang.reflect.UndeclaredThrowableException)EXP就利用失敗,這個點遇到的問題卡了我兩天左右,最后看到有人復現成功,測試的9版本,所以我就切換到9版本。

在9版本中修復了這個第三方庫的BUG,不主動的調用Log,加了if判斷,如下:

9.0 版本中org.apache.aries.jndi.ObjectFactoryHelper#getObjectInstanceViaContextDotObjectFactories函數的實現:


8.5.5.0 中的org.apache.aries.jndi.ObjectFactoryHelper#getObjectInstanceViaContextDotObjectFactories實現:


很明顯加了判斷,就沒這個問題了。

所以8.5.5.0默認情況下有可能打不死(如果不打補丁2013年的那個庫之前),9.0.0.2 沒問題,其他版本后續慢慢測試。

補個成功截圖:





參考

https://www.thezdi.com/blog/2020/7/20/abusing-java-remote-protocols-in-ibm-websphere


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM