CVE-2020-14644 weblogic RemoteConstructor RCE via T3 protocol
2022-12-11 22:28:0 Author: xz.aliyun.com(查看原文) 阅读量:52 收藏

前言

这里学习了Weblogic中的两条CVEs,在反序列化调用readResolve方法进而调用defineClass方法进行类的加载,并执行我们的恶意逻辑,同样也通过寻找不同于CVE-2020-2883中使用的extract方法,来绕过之前的CVE的黑名单过滤

环境搭建

参考前面的环境

T3

CVE-2020-14644

原理

这个CVE主要是找到了一个在黑名单防护外的一个类RemoteConstructor,在该类进行反序列化调用过程中将会调用defineClass方法进行任意类的加载,并在之后进行了实例化的操作,造成了静态代码块中的恶意代码的执行

分析

前面在原理的部分已经讲了该CVE的漏洞触发点,我们仔细的跟进一下,从漏洞原理到POC编写来学习一下这个CVE的利用方式

对于RemoteConstructor这个类,我们首先来看一下他的继承关系

这个类实现了ExternalizableLite接口,且该接口继承了Serializable接口,所以该类是能够进行反序列化方法的调用的

但是我们可以发现在该类中是不存在有readObject方法的实现的,但是存在有readResolve方法的实现,在反序列化的过程中将会调用这个方法

首先,我们来认识一下readObject方法和readResolve方法的区别

readResolve方法用于替换从流中读取的对象,将其替换为单例模式,即是确保了没有人可以通过反序列化或者序列化单例来创建另一个实例

当ObjectInputStream从流中读取对象并准备将其返回给调用者时,将调用readResolve方法。 ObjectInputStream检查对象的类是否定义readResolve方法。如果定义了方法,则调用readResolve方法以允许流中的对象指定要返回的对象。返回的对象应该是与所有用途兼容的类型。如果不兼容,则在发现类型不匹配时将抛出ClassCastException

总结一下,就是在调用readObject方法之后将会紧接着调用readResolve方法,能够覆盖readObject的内容

好了,回到代码中来

在反序列化过程中在调用readResolve方法的时候将会调用该类的newInstance方法

在这个方法中首先调用了getClassLoader方法获取类加载器

也就是该类的m_loader属性值,但是该类的这个属性值是被transient修饰的,是不能够在反序列化中保存数据的,所以此时这里的m_loader属性为null

所以返回的是Base.getContextClassLoader(this)返回的类加载器

之后我们回到newInstance方法中来,调用了返回的RemotableSupport类对象的realize方法

这个方法传入的参数也就是我们在序列化时的RemoteConstructor类,在调用realize方法的开头,调用了RemoteConstructor#getDefinition方法获取类中的m_definition属性值

要保证这里的这个属性值不能为null,为什么呢?因为在之后将这个属性值传入RemotableSupport#registerIfAbsent方法中进行调用过程中,将会抛出异常

这里使用assert断言了传入的definition参数值不为null值

所以我们看看m_definition属性的来源

RemoteConstructor类的构造方法中存在有对m_definition属性的赋值操作,所以我们需要创建一个有效的ClassDefinition类对象进行传入

来到ClassDefinition类的构造方法中

也就是需要创建一个ClassIdentity类对象和一个字节数组进行传入,具体应该传入什么,我们之后会提及

现在回到RemotableSupport#realize方法的调用中来

经过上面的分析,已经获得了我们自定义的ClassDefinition类对象,之后在该方法中调用了该对象的getRemotableClass方法获取远程类,怎么获取这个远程类的呢?我们跟进看下具体的实现代码

什么?直接返回了m_clz这个属性值!

好巧不巧,这个属性值因为被transient所修饰,也就是值是为null的,走不通了,不能通过这里传入我们的恶意类

回到readlize方法中

这里如果没有获取到远程类对象,将会调用RemotableSupport#defineClass方法进行获取,传入的参数是我们自定义的ClassDefinition对象

这里将会将definition.getId().getName()作为远程类的类名

这里getId方法返回的是我们在前面的ClassDefinition构造方法中传入的第一个参数,也即是ClassIdentity对象

而在ClassIdentity#getName方法中

这里的getPackage方法返回的是m_sPackage属性值,这里的getSimpleName方法为:

也即是将m_sBaseName属性值+$+m_sVersion属性值

所以,对于getName返回的值主要就是m_sPackage/m_sBaseName$m_sVersion这种格式的值

我们接下来可以看看这些属性值的赋值方法,来到其构造方法中来

这里我们只需要传入一个恶意类作为参数就行了,在其构造方法中将会自动获取包名 / 类名 / 将类的md5摘要值转为16进制串,之后通过重载方法进行赋值操作

分析到这里,我们在创建ClassDefinition类对象的第一个参数就大致清楚了

ClassIdentity classIdentity = new ClassIdentity(test1.class);
new ClassDefinition(classIdentity, xxxxx)

这里传入的test1类对象就是我们的恶意类对象

之后我们回到defineClass方法中看看第二个参数应该如何构造

最后在调用defineClass方法进行类的加载中传入的字节数组是abClass变量,该变量的值是通过调用definition.getBytes方法获得的

也即是返回的m_abClass属性值,也即是在ClassDefinition构造方法中传入的第二个参数值设定的

还有我们值得注意的一点是,在defineClass方法返回的类对象限定了是一个实现了Remotable接口的类,所以,在我们的恶意类中必须要实现这个接口

但是,这里还存在有一个问题,就是在将类名和字节数组传入defineClass方法获取类的过程中,传入的类名,并不是我们的恶意类的类名

而是我们在前面提到的m_sPackage/m_sBaseName$m_sVersion这种格式的类名,所以如果直接获取我们恶意类的字节码数组是不能够成功达到我们的恶意目的,这里我们需要将我们恶意类的类名改成对应的格式之后进行传入

直接使用javassist进行修改

到这里,我们就可以构造出一个关键的POC代码了

ClassIdentity classIdentity = new ClassIdentity(test1.class);
ClassPool cp = ClassPool.getDefault();
CtClass ctClass = cp.get(test1.class.getName());
ctClass.replaceClassName(test1.class.getName(), test1.class.getName() + "$" + classIdentity.getVersion());
RemoteConstructor constructor = new RemoteConstructor(
        new ClassDefinition(classIdentity, ctClass.toBytecode()),
        new Object[]{}
);

在设置了远程类之后就是通过实例化来触发我们的静态代码块了,没什么好分析的

POC:

直接将上面得到的RemoteConstructor类对象进行序列化之后通过T3协议发送即可,当然也可以使用IIOP进行利用

调用栈:

exec:347, Runtime (java.lang)
<clinit>:9, test1$2C7B1D9B24203562AE41FAE89389A68A (pers.weblogic)
allocateInstance:-1, Unsafe (sun.misc)
allocateInstance:439, DirectMethodHandle (java.lang.invoke)
newInvokeSpecial__L:-1, 112302969 (java.lang.invoke.LambdaForm$DMH)
reinvoke:-1, 1487470647 (java.lang.invoke.LambdaForm$BMH)
invoker:-1, 574568002 (java.lang.invoke.LambdaForm$MH)
invokeExact_MT:-1, 952486988 (java.lang.invoke.LambdaForm$MH)
invokeWithArguments:627, MethodHandle (java.lang.invoke)
createInstance:149, ClassDefinition (com.tangosol.internal.util.invoke)
realize:142, RemotableSupport (com.tangosol.internal.util.invoke)
newInstance:122, RemoteConstructor (com.tangosol.internal.util.invoke)
readResolve:233, RemoteConstructor (com.tangosol.internal.util.invoke)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invokeReadResolve:1260, ObjectStreamClass (java.io)
readOrdinaryObject:2078, ObjectInputStream (java.io)
readObject0:1573, ObjectInputStream (java.io)
readObject:431, ObjectInputStream (java.io)

修复

  1. 安装补丁将其加入黑名单
  2. 限制T3数据包来源
  3. 禁用IIOP协议

文章来源: https://xz.aliyun.com/t/11945
如有侵权请联系:admin#unsafe.sh