cve 2020-14644 分析利用以及回显构造

0x00 前置知识

1. classloader defineClass

该方法允许将符合class格式的bytes数组,作为类去加载。该方法返回一个Class对象。

一般情况下,通过defineClass加载的类,不允许同名,否则会报错,切记

2. java asm& javaassist

既然上面说了,defineClass不允许加载同名类。所以我们需要动态构建class,并生成bytes数组。在java中一般使用asm或者javaassist通过jvm字节码,直接生成一个class。两种各有优缺点。

  1. asm语法复杂,需要熟悉jvm底层知识。运行速度快
  2. javaassist 语法简单,运行速度慢

在这里推荐一个asm插件,可以将class一键转换为asm的java代码,并输出bytes。然后再按需修改class的相关参数

3. java反序列化的transient参数

protected transient Class<? extends Remotable> m_clz;
protected transient MethodHandle m_mhCtor;

transient参数修饰的变量,是不参与反序列化的。最终变量值为null

0x01 cve-2020-14644 漏洞分析

简单来讲,该漏洞在反序列化时通过defineClass直接加载类。下面是待加载的类

下面我们从RemoteConstructor的newInstance开始分析,断点设置以及调用堆栈如下

newInstance的代码如下

    public T newInstance() {
        RemotableSupport support = RemotableSupport.get(this.getClassLoader());
        return support.realize(this);
    }

RemotableSupport可以认为是coherence自定义的Classloader。RemotableSupport中实现了defineClass。下面我们跟入RemotableSupport的realize方法,看一下realize方法都做了什么

    public <T> T realize(RemoteConstructor<T> constructor) {
        ClassDefinition definition = this.registerIfAbsent(constructor.getDefinition());
        Class<? extends Remotable> clz = definition.getRemotableClass();
        if (clz == null) {
            synchronized(definition) {
                clz = definition.getRemotableClass();
                if (clz == null) {
                    definition.setRemotableClass(this.defineClass(definition));
                }
            }
        }

        Remotable<T> instance = (Remotable)definition.createInstance(constructor.getArguments());
        instance.setRemoteConstructor(constructor);
        return instance;
    }

首先从RemoteConstructor中获取m_definition,类型为ClassDefinition。随后调用m_definition的getRemotableClass方法。这一句是关键。我们去看一下m_definition的getRemotableClass方法

可以很明显的看到getRemotableClass方法返回的m_clz,是通过transient修饰的。所以在反序列化后,这里一定返回null。

于是就会走到if条件的另外一个分支,调用this.defineClass(definition)去加载类。通过RemotableSupport去根据给定的参数,创建一个类。

RemotableSupport的defineClass方法就很简单了。从ClassDefinition中获取类名,类的bytes字节,调用系统的classloader去创建一个类

    protected Class<? extends Remotable> defineClass(ClassDefinition definition) {
        String sBinClassName = definition.getId().getName();
        String sClassName = sBinClassName.replace('/', '.');
        byte[] abClass = definition.getBytes();
        definition.dumpClass(DUMP_REMOTABLE);
        return this.defineClass(sClassName, abClass, 0, abClass.length);
    }

所以payload代码如下

在这里我们只需要通过iiop协议,直接将RemoteConstructor对象发送给weblogic服务即可

0x02 回显利用

在cve-2020-2551 回显利用中,因为classloader的问题,我们需要上传一个jar包,并区分windows与linux操作系统才可以通过URLClassloader去正确加载上传的jar包。但是通过该漏洞,我们发现,可以直接写好类,并转换为bytes字节,通过反序列化发送给weblogic服务器即可。

至于回显,无非就是给服务器安装一个JNDI实例即可。所以我们需要加载的类的代码如下

运行结果如下

目前代码已上传至 GitHub - potats0/cve_2020_14644

0x03 参考

  1. CVE-2020-14644 分析与 gadget 的一些思考