首页 > 关于安博通 > 新闻中心
【漏洞分析】WebLogic 12.2.1.4.0远程代码执行漏洞(CVE-2020-14645)
2020-09-24
148

一、漏洞概述

 

WebLogic是美国Oracle公司出品的application server,确切的说是基于JavaEE架构的中间件。WebLogic是用于开发、集成、部署和管理大型分布式Web应用、网络应用和数据库应用的Java应用服务器,可将Java的动态功能和Java Enterprise标准的安全性引入大型网络应用的开发、集成、部署和管理中。

 

由于针对上一个漏洞的补丁修复不全面,导致Oracle Coherence存在绕过方式。通过该漏洞对T3协议进行利用,攻击者可实现远程代码执行,进而控制服务器。该漏洞利用复杂度低、风险高,建议尽快修复。

 

二、影响范围

 

WebLogic 12.2.1.4.0

 

注:目前只确定WebLogic 12.2.1.4.0存在该漏洞,与CVE官网给出的影响范围有出入,原因是该漏洞利用的com.tangosol.util.extractor.UniversalExtractor类只存在于WebLogic 12.2.1.4.0的coherence.jar中。

 

三、修复建议

 

建议安装WebLogic官方发布的最新补丁,更新补丁前请先做好备份。

 

推荐使用JDK版本大于6u211、7u201、8u191启动的WebLogic,可以防止JNDI注入。

 

四、漏洞分析

 

该漏洞是针对CVE-2020-2555的绕过利用。

 

CVE-2020-2555使用了coherence.jar中com.tangosol.util.extractor.ReflectionExtractor类中的invoke函数实现RCE。而CVE-2020-14645则使用了coherence.jar中的com.tangosol.util.extractor.UniversalExtractor类达到相同效果,同时绕过了前一个漏洞修复补丁中的黑名单限制。

 

可以首先看到UniversalExtractor类的如下代码:

 

1.jpg

 

这里出现了一个invoke方法,但这里并不是此次利用的注入点,下面来分析一下原因。

 

从代码中可以看出,要想进入此处逻辑,首先需要保证targetPrev不为空,而该对象被赋予的值是this.m_cacheTarget。在m_cacheTarget被声明时可以看到一个关键修饰符transient,以下是这个修饰符的介绍:

 

一个对象只要实现了Serilizable接口,这个对象就可以被序列化,Java的这种序列化模式为开发者提供了很多便利。不必关注具体序列化的过程,只要这个类实现了Serilizable接口,这个类的所有属性和方法都会自动序列化。

 

然而在实际开发过程中,常常会遇到这样的问题,这个类的有些属性需要序列化,而其他属性不需要被序列化。举个例子,如果一个用户有一些敏感信息(如密码、银行卡号等),为了安全起见,不希望在网络操作(主要涉及序列化操作,本地序列化缓存也适用)中被传输,这些信息对应的变量就可以加上transient关键字。换句话说,这个字段的生命周期仅存在于调用者的内存中,而不会写到磁盘里持久存在。

 

总之,Java的transient关键字为使用者提供了便利,只需要实现Serilizable接口,在不需要序列化的属性前添加关键字transient,序列化对象时这个属性就不会被序列化到指定目的地。

 

这说明m_cacheTarget不会经过序列化过程,因此在被攻击的电脑上执行反序列化时,这个对象一定为null,即根本无法进入if条件。(注:当然这个注入点也可以使用,只要调用两次compare方法,即可对m_cacheTarget进行赋值,进入if条件。




那么这次利用的注入点在哪呢?接着往下看,此处有一个extractComplex方法。

 

2.jpg

 

跟进该方法后,又发现一个invoke 。

 

3.jpg

 

这就是此次利用的注入点。前面提到,该漏洞与CVE-2020-2555有异曲同工之处,原因在于该漏洞也利用了oTarget参数的用户可控特性,也就是说可以通过该参数对这个注入点进行注入,从而完成攻击。

 

接下来用网上公布的Poc来分析一下该漏洞的利用链,附上该Poc代码:

 

4.jpg

 

可以看到,该Poc利用的是PriorityQueue这个类进行序列化。之所以使用这个类,是因为它实现了Serializable接口,而且其重写的readobject( )方法最终可以调用extract( )方法,控制传入的参数,在下个断点来看看具体的调用。

 

首先根据这个Poc生成一个序列化文件,然后直接通过本地方法进行反序列化,观察其利用链(这里也可以使用远程调试的方法,这样流程更直观,但利用链是一样的),序列化与反序列化相关代码如下:

 

5.jpg

 

先将断点下到extractComplex( )方法的入口处,即第48行,然后可以看到当前的调用链。

 

6.jpg

 

从这里可以看出,在执行了PriorityQueue的readObject( )方法后,调用了其中的heapify( )、siftDown( )等方法。而在siftDown( )方法中,如果在生成PriorityQueue对象时定义了比较器,那么就会调用siftDownUsingComparator( )方法。

 

7.jpg

 

而在这个Poc中传入的是ExtractorComparator比较器,该比较器针对UniversalExtractor类的对象,因此在进入siftDownUsingComparator( )方法后,会调用ExtractorComparator重写的compare( )方法。


8.jpg

 

compare( )方法中使用的extract( )方法,则是UniversalExtractor类重写的extract( )方法。

 

9.jpg

 

入口链已经找到了(这个入口链在很多coherence的漏洞中也被利用了,如CVE-2020-2883),接下来就需要找到从extract( )到invoke( )的链。



 

刚刚把断点下在了extractComplex( )方法的入口处,接下来进入该方法,观察其利用链,重点看一下getCanonicalName( )方法。

 

10.jpg

 

跟着getValueExtractorCanonicalName( ),这个方法的返回值一定为空(原因是这个函数里有一个判断,判断传入的对象是否为AbstractRemotableLambda类的对象,如果不是返回null;在这里传this,返回一定为空),即必会进入后续的if条件,接着继续跟computeValueExtractorCanonicalName( )方法。

 

11.jpg

 

这里的逻辑是:判断sName是否以( )结尾,如果不是则直接返回函数名。然后判断aoParam的内容,如果不为空或长度大于0就会返回空,所以这里可调用的方法必须是无参数的。最后进入到sName的判断,查看该方法是否以get或is开头,最后将前缀和后缀去掉,返回小写函数名。

 

回到extractComplex( )方法,接下来会有一个fProperty变量的判断,该变量赋值与isPropertyExtractor( )的返回值有关,而isPropertyExtractor( )返回的是m_fMethod属性的反值,这个属性在init( )时被赋予了false,所以必定进入下面的if条件。

 

12.jpg

 

这个if条件里的逻辑是首先对BEAN_ACCESSOR_PREFIXES(一个String数组,只有“get”和“is”两项)进行遍历,然后对函数名进行拼接,也就是对返回的sCName进行get和is的拼接,然后到clzTarget对象指向的类中去寻找该函数是否存在。

 

如果这个类中存在这个对象,后续就会为m_cacheTarget赋值,最后进入invoke( )进行反射调用。



 

综上所述,需要找到一个可调用的函数,该函数必须以get或is开头,且没有参数,最终能执行JNDI注入。

 

在该Poc中使用的是com.sun.rowset.JdbcRowSetImpl这个JDK的内置类,该类的getDatabaseMetaData( )完全符合上述条件。

 

13.jpg

 

跟进这个函数的connet( )方法。

 

14.jpg

 

关键的一点是,只要能控制getDataSourceName( )的返回值,就能进行JNDI注入。然后看看getDataSourceName( )方法。

 

15.jpg

 

一目了然,直接使用JdbcRowSetImpl的对象调用setDataSourceName( )设置ladp目录,攻击完成。

 

五、相关链接

 

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

github.com/Y4er/CVE-2020-14645