T3反序列化 Weblogic12.2.1.4.0 JNDI注入

Smi1e@Pentes7eam

近日,Oracle官方发布安全更新,其中包含Weblogic的安全补丁,本着技术研究角度,和大家分享、探讨其中的一个JNDI注入漏洞。

在此前的CVE-2020-2883 将 extract 方法存在危险操作的 MvelExtractor 和 ReflectionExtractor 两个类加入到了黑名单中,因此我们只需要继续找一个 extract 方法存在危险操作的类即可绕过补丁,这里找到的是 Weblogic 12.2.1.4.0 Coherence 组件特有的类 com.tangosol.util.extractor.UniversalExtractor,因此只能影响 Weblogic 12.2.1.4.x。

 

影响范围

Oracle WebLogic Server 12.2.1.4.0

 

漏洞报告

入口同 CVE-2020-2883,利用 java.util.PriorityQueue 反序列化时会间接调用 com.tangosol.util.ValueExtractor 接口任意实现类的 extract 方法。

在 CVE-2020-2883 中我们可以使用 com.tangosol.coherence.rest.util.extractor.MvelExtractor 的 extract 方法执行mvel表达式或者使用 com.tangosol.util.extractor.ReflectionExtractor 的 extract 方法反射调用任意方法。但是补丁把他们都加入到了黑名单中,因此我们需要再找一个 extract 方法有危险操作且实现了 com.tangosol.util.ValueExtractor 接口的类。

我这里找到的是 com.tangosol.util.extractor.UniversalExtractor

public class UniversalExtractor<T, E> extends AbstractExtractor<T, E> implements ValueExtractor<T, E>, ExternalizableLite, PortableObject {

    public static final String[] BEAN_ACCESSOR_PREFIXES;

    public static final String METHOD_SUFFIX = "()";

    @JsonbProperty("name")

    protected String m_sName;

    @JsonbProperty("params")

    protected Object[] m_aoParam;

    protected transient String m_sNameCanon;

    private transient TargetReflectionDescriptor m_cacheTarget;

    private transient boolean m_fMethod;

      ......

}

看其 extract 方法,虽然if条件中有一个 invoke 操作,但是 this.m_cacheTarget 使用了 transient 修饰导致无法被序列化。

跟进else条件中的 extractComplex 方法,该方法中也有一个 invoke 操作,虽然我们可以控制参数 oTarget 和 aoParam ,但是 method 对象的获取过程也有一个if条件。

ClassHelper.findMethod 方法通过类、方法名以及方法的参数类型数组来反射返回该类中的特定方法。因此只要我们进入else条件中,即可调用任意类的任意方法。

进入else条件的前提是 this.isPropertyExtractor() 返回false,也就是 this.m_fMethod 为true,但是该成员变量依然使用 transient 修饰,无法序列化,因此我们只能寄希望于if条件中。

public boolean isPropertyExtractor() {

    return !this.m_fMethod;

}

看下if条件

protected E extractComplex(T oTarget) throws InvocationTargetException, IllegalAccessException {

         ......

    String sCName = this.getCanonicalName();

    boolean fProperty = this.isPropertyExtractor();

    Method method = null;

    if (fProperty) {

        String sBeanAttribute = Character.toUpperCase(sCName.charAt(0)) + sCName.substring(1);



        for(int cchPrefix = 0; cchPrefix < BEAN_ACCESSOR_PREFIXES.length && method == null; ++cchPrefix) {

            method = ClassHelper.findMethod(clzTarget, BEAN_ACCESSOR_PREFIXES[cchPrefix] + sBeanAttribute, clzParam, false);

    }else{

      ......

    }

}

跟进 sCName 的赋值过程 this.getCanonicalName() ,因为this对象的原因,Lambdas.getValueExtractorCanonicalName 无论如何都会返回null。

接着看下 CanonicalNames.computeValueExtractorCanonicalName ,如果 aoParam 不为 null 且数组长度大于0就会返回 null ,因此我们可调用的方法必须是无参的。接着如果方法名 sName 不以 () 结尾,则直接返回方法名。否则会判断方法名是否以 VALUE_EXTRACTOR_BEAN_ACCESSOR_PREFIXES 数组中的前缀开头,是的话就会截取掉并返回。

回到 extractComplex 方法中,在if条件里会对上面返回的方法名做首字母大写处理,然后拼接 BEAN_ACCESSOR_PREFIXES 数组中的前缀判断 clzTarget 类中是否含有拼接后的方法,此时会发现我们现在无论如何只能调用任意类中 get 和 is 开头的方法,并且是无参的,条件有些苛刻。

不过仔细观察发现 UniversalExtractor#extract 方法可以调用两次,我们可以利用第一次调用改变 UniversalExtractor 对象的关键成员变量值,在第二次调用时完成利用。

接下来我想了三个思路:

  1. 直接去找所有类中 get 和 is 开头并且可利用的无参方法
  2. 想办法调用 init 方法对 this.m_fMethod 进行赋值,从而令 fProperty 的值为false并进入else条件中。

extractComplex 方法中对 this.m_cacheTarget 进行了赋值,因此我们第二次调用 UniversalExtractor#extract 方法时可以进入到if条件中执行 targetPrev.getMethod().invoke() 。

由于我们只能调用 get 和 is 开头的方法,因此思路2不行。由于我们在利用 targetPrev.getMethod().invoke() 调用任意方法时,传入的参数和 extractComplex 方法中 findMethod 的参数是同一个,导致如果我们调用非 get 和 is 开头的方法时,findMethod 会返回 null ,从而在 method.invoke 时会报错,这样的话就无法第二次调用 UniversalExtractor#extract 方法,因此思路3也不行。

最终只能用思路1去全局搜 get 和 is 开头且存在危险操作的无参方法。虽然只能调用无参方法,但是我们可以通过序列化控制对象的成员变量来获取一些可控点,当然该方法所在的类必须是可序列化的,另外该方法的危险操作涉及到的对象依然需要可序列化,否则无法利用。

这个寻找过程和fastjson有些相似,最终找到了 com.sun.rowset.JdbcRowSetImpl 这个jdk内置类,看下其 getDatabaseMetaData() 方法。

跟进 this.connect() ,可以发现只要 this.getDataSourceName() 可控,我们就能进行JNDI注入。

getDataSourceName() 调用的是父类 javax.sql.rowset.BaseRowSet 的,其 dataSource 属性是可序列化的,因此我们可以控制 this.getDataSourceName() 的返回值。

private String dataSource;

漏洞证明

由于Weblogic在JEP290机制下做的是全局反序列化过滤,而JNDI在jdk高版本依赖于本地Class的反序列化链,因此在Weblogic中JNDI注入只能打 JDK6u211、7u201、8u191 之前的版本。

 

补丁修复方式

分析Weblogic补丁p31537019_122140_Generic.zip

看到最新补丁的黑名单中不仅过滤了 com.tangosol.util.extractor.UniversalExtractor ,还过滤了 com.tangosol.util.extractor 包下的其他几个类,另外也把com.tangosol.coherence.rest.util.extractor、com.tangosol.internal.util.invoke.lambda 等一些包加入了黑名单中。

(完)