CVE-2020-4450:WebSphere远程代码执行漏洞分析

 

作者:Hu3sky@360CERT

0x01 漏洞简述

2020年06月08日,360CERT监测到 IBM官方发布了 WebSphere远程代码执行 的风险通告,该漏洞编号为 CVE-2020-4450,漏洞等级:严重,漏洞评分:9.8分

此漏洞由IIOP协议上的反序列化造成,未经身份认证的攻击者可以通过IIOP协议远程攻击WebSphere Application Server,在目标服务端执行任意代码,获取系统权限,进而接管服务器。

对此,360CERT建议广大用户及时安装最新补丁,做好资产自查以及预防工作,以免遭受黑客攻击。

 

0x02 风险等级

360CERT对该漏洞的评定结果如下

评定方式 等级
威胁等级 严重
影响面 广泛
360CERT评分 9.8分

 

0x03 影响版本

  • WebSphere Application Server: 9.0.0.0 to 9.0.5.4

  • WebSphere Application Server: 8.5.0.0 to 8.5.5.17

  • WebSphere Application Server: 8.0.0.0 to 8.0.0.15

  • WebSphere Application Server: 7.0.0.0 to 7.0.0.45

 

0x04 漏洞详情

按照zdi给出的分析,iiop的拦截是在com.ibm.ws.Transaction.JTS.TxServerInterceptor#receive_request,那么下断点进行远程调试,由于websphere自己实现了一套iiop,所以想要走到iiop触发反序列化的点还需要构造满足条件的iiop客户端,这里需要走到demarshalContext方法,前提是满足validOtsContexttrue,也就是需要serviceContext不为null

假如我们已经构造了serviceContext不为null,继续往下看,将serviceContext.context_data传入demarshalContext方法。 调用createCDRInputStream创建CDRInputStream,实际上生成的是EncoderInputStreamCDRInputStream的子类,之后调用EncoderInputStream#read_any方法。

之后的调用有些繁琐,就不列出来了,调用栈为:

serviceContext赋值

由于需要serviceContext不为null,才能走到demarshalContext方法体里面,在com.ibm.rmi.iiop.Connection#setConnectionContexts方法中,该方法如下:

setConnectionContexts方法可以对ServiceContext属性进行设置,但是我们需要从iiop生成的默认上下文中获取存储着的当前Connection信息。

参考@iswin师傅的文章,可以知道,在com.ibm.rmi.iiop.GIOPImpl里,存在一个getConnection方法,可以获取当前上下文的Connection实例对象,

不过该方法需要传递当前ior参数,而GIOPImpl的对象在orb里,

这些都能通过反射从defaultContext中获取。

恶意数据流构造

看一下数据流是怎么被解包的,具体在demarshalContext方法里,也就是构造完ServeicContext下一步要执行的。

与之对应的,marshalContext方法里有相应的数据包生成代码,所以只需要将关键代码单独掏出来,再把PropagationContext对象构造一下,就能生成gadgets对象的数据流。

构造的关键代码为:

gadgets

既然iiop已经通了,那么我们就根据zdi给出的gadgets进行构造,首先需要明确的是,反序列化的入口是org.apache.wsif.providers.ejb.WSIFPort_EJB(因为websphere自身类加载器的问题,导致现有的gadgets都无法利用,所以我们只能基于新的挖掘的类来构造gadgets)。

这里我们利用的是handle.getEJBObject方法,handle是一个Handle类型,在实现了Handle接口的类中,能够进行利用的是com.ibm.ejs.container.EntityHandle这个类,事实上,我们在对handle进行赋值的时候,比较复杂,需要反射多个对象。

我们来看一下他的getEJBObject方法。 这里有几处需要注意的:

lookup加载本地factory

先来看第一处,也就是lookup方法,这里的homeJNDIName是我们在反序列化流程中可以控制的。于是,在能够出网的情况下,可以指向我们的rmi服务。

这里首先会调用registry.lookup,获取我们在rmibind的对象,由于jdk版本过高的原因,所以导致com.sun.jndi.ldap.object.trustURLCodebase选项默认被设置为false,也就是说,我们不能利用jndi去远程服务器上利用URLClassLoader动态加载加载类,只能实例化本地的Factory,这里利用的方式是加载本地类,具体细节参考:Exploiting JNDI Injections in Java

简单来说,正常情况下,jndi的利用会在RegistryImpl_Stub.lookup之后返回一个ReferenceWrapper的客户端代理类,ReferenceWrapper提供了三个参数,className,factory,factoryLocation,如果本地加载不到className,那么就会去factoryLocation上加载factory,大致流程为:

而现在不能远程去加载factoryLocation,那么我们寻求一个本地factory来实例化,并利用该factorygetObjectInstance方法,根据zdi提供的漏洞细节,满足条件的factoryorg.apache.wsif.naming.WSIFServiceObjectFactory

前边的调用栈是这样的,

com.sun.jndi.rmi.registry.RegistryContext#lookup
com.sun.jndi.rmi.registry.RegistryContext#decodeObject
javax.naming.spi.NamingManager#getObjectInstance

rmi服务端可以在reference对象里对factory进行设置,当然这个factory需要满足一些条件,当调用WSIFServiceObjectFactory.getObjectInstance,我们看一下这个方法。这里wsdlLoc,serviceNS等值都可以在rmi端通过Reference进行设置。

这里会对ref进行判断,也就是Reference对象的第一个参数,也是可控的。我们需要走到下面的判断里,也就是让refWSIFServiceStubRef,原因是这样的,需要回过来看到前面, 我们需要指定返回代理的类型是EJBHome。这里有两个地方需要指定接口的类型,一个是narrow第二个参数homeClass,一个是在Reference指定className,用来控制返回的代理类型。

WSIF web服务

这里的getObjectInstance调用将从远程URL初始化WSIF服务,该URL指向可由攻击者控制的远程XML定义,在远程xml里,我们可以将方法进行映射,这里只说个概念,后面再仔细说。

指定生成的stub的接口类型

refWSIFServiceStubRef类型的时候,可以通过className来指定生成stub的接口类型,这样就能生成实现EJHome接口的代理,这里前面已经提到过了,具体在rmi服务端通过Reference进行设置。

创建动态代理

这里会在getStub里创建代理类。

根据提供的接口,最终返回WSIFClientProxy代理类。

el表达式注入

接着,在this.findFindByPrimaryKey获取homeClass接口的findByPrimaryKey方法。

之后,就会调用动态代理类的invke方法,传入findFindByPrimaryKeythis.key,也就是方法的参数。

WSIFClientProxy.invoke的方法里,会调用WSIFPort实现类的createOperation方法。

这个createOperation方法就能将方法进行映射,这里我们可以将findFindByPrimaryKey方法映射为本地存在的方法,比如javax.el.ELProcessoreval方法,这里的映射就主要体现在之前提到的WSIF web服务里,需要将映射内容体现在我们自定义的远程xml文件里。主要语法可以参考:WSDL Java Extension,具体的调用就在自定义rmi服务上进行设置,之后的调用栈如下:

最终在ELProcessor#eval方法里执行el表达式。

漏洞利用

利用成功的截图如下:

版本修复

在官网下载补丁进行分析,发现对反序列化入口WSIFPort_EJB进行了修改,在readObject方法里,将原本的handle.getEJBObject方法给取消了,这样,也就把这个链的入口给杀死了。

 

0x05 时间线

2020-06-04 IBM发布预警

2020-06-08 360CERT发布预警

2020-07-21 ZDI发布分析报告

2020-08-05 360CERT发布分析报告

 

0x06 参考链接

  1. CVE-2020-4450: WebSphere远程代码执行漏洞通告
  2. HubL中的EL注入导致远程代码执行
  3. ABUSING JAVA REMOTE PROTOCOLS IN IBM WEBSPHERE
  4. Websphere CVE-2020-4450漏洞分析
  5. Exploiting JNDI Injections in Java
  6. WSDL Java Extension
(完)