看了很多網上關於weblogic t3協議解析,基本沒人好好分析。
先說一下為什么要分析T3協議,主要是受朋友所托使用python模擬調用T3協議。目前的weblogic T3攻擊工具,大體都是java或者python等編寫,有兩大特點:
- java 編寫的攻擊工具一般集成weblogic的t3.jar,攻擊者通過反序列化漏洞造成的任意代碼執行向weblogic安裝一個T3實例,攻擊者調用這個實例去完成回顯等復雜操作。
- python等語言編寫的工具為了實現weblogic反序列化攻擊,一般直接替換weblogic t3流中
aced 0005
部分實現反序列化攻擊。這種方式的缺點在於無法完成T3協議的交互,導致無法回顯等弊端。
而網上關於weblogic t3協議根本搜索不到任何相關信息,因為T3協議是oracle獨有的,非開源協議。如果要是分析T3協議,只能對着weblogic 的源碼,靜態分析加動態調試。只知道T3也稱為豐富套接字,是BEA內部協議,功能豐富,可擴展性好。T3是多工雙向和異步協議,經過高度優化,只使用一個套接字和一條線程。借助這種方法,基於Java的客戶端可以根據服務器方需求使用多種RMI對象,但仍使用一個套接字和一條線程。這也為我們靜態分析t3協議帶來了很多麻煩
T3的交互過程如下
協議協商
客戶端首先發送下面的信息給weblogic服務器
t3 10.3.6
AS:255
HL:19
表明這是一個T3協議,而服務器接收到信息后,也會回復類似的消息。
HELO:12.2.1.4.false
AS:2048
HL:19
MS:10000000
PN:DOMAIN
代碼分析有點復雜,這里不再贅述,只講重點。
客戶端通過下面的代碼發起socket請求
weblogic.rjvm.t3.MuxableSocketT3#connect(java.net.InetAddress, int, int)
服務端則在weblogic.rjvm.t3.MuxableSocketT3#readIncomingConnectionBootstrapMessage處理T3的啟動。當然,每個頭的含義可以在weblogic.rjvm.MsgAbbrevJVMConnection找到詳細的定義。
一般而言,AS與HL頭比較常見,下面重點說一下這兩個頭
- HL頭,標識自己后面發起的t3的協議頭長度。
- AS頭,因為T3的反序列化使用了一個特別的數據結構Stack,AS頭用來標識這個stack的容量,這個與T3協議反序列化分隔符
FFFE0001
相關。現在我們先不理會這個頭的具體含義
通信雙方根據對方發來的協議協商信息,開始建立連接
T3協議分析
再協商完畢上面的信息后,由客戶端向服務端發送自己的詳細信息,這個叫peerinfo。從這里開始就全部是二進制流了,變得不可讀。我們需要開始分析T3協議的每個bytes
每個T3協議前4位bytes標識本次請求的數據長度,這個沒什么好說的
在weblogic.rjvm.MsgAbbrevJVMConnection#dispatch處,解讀T3協議流,並處理
T3協議頭處理
我們首先看一下協議頭是怎么被處理的
相關代碼在weblogic.rjvm.MsgAbbrevInputStream#init中。首先調用readHeader
這些內容加在一起正好是19字節,也就是上一階段協商中HL的內容。每個字段的含義如下,因為我也是通過逆向分析得到的結果,有些可能不太准確。
cmd
表明本次請求的類型。請求類型一共有十余種,值分別如下
其中,T3交互peerInfo初始化,類型為CMD_IDENTIFY_REQUEST
,執行rebind lookup等操作,cmd類型為CMD_REQUEST
。t3也支持c#調用
flags
標志位
responseId
這個頭的作用為標識每條流的請求順序,是自增的,初始值為-1。一般而言,服務端的responseId設置與客戶端在本次請求的responseId值相同,這個字段有點類似於tcp 的syn。相關代碼如圖
invokeableId
這個字段比較重要。客戶端將根據這個字段的值去查找響應的處理程序。t3與java rmi不同之處在於,java rmi協議在客戶端的lookup查找對象中返回該對象的動態代理。t3返回一個數字代表該對象。后續操作中,設置invokeableId為該對象代表的數字,完成rpc調用。
如果客戶端執行的是rebind,交換彼此信息,lookup操作,則 invokeableId為9。不要問我為什么,wlc就這樣設計的。具體有哪些invokeableid,可以在OIDManager
中查看。
當然,如果是響應,這種情況下invokeableId將不再重要。weblogic一般設置為與responseid相同。
abbrevOffset
abbrev這個數據結構在本次請求中相對於開始部分的偏移
T3 Abbrev 處理
讀取完頭信息后,開始讀取MsgAbbrevs。這個數據結構我不能很好的描述它,因為T3協議並沒有全部實現java反序列化協議,而是自己由魔改了一部分。比如readClassDescriptor的class部分,T3協議在abbrevs中讀取。這個后面將會講到,現在你不理解也無所謂
這里將會跳轉到abbrevOffset標識的部分並開始讀取數據。代碼如下
void read(MsgAbbrevInputStream in, BubblingAbbrever at) throws IOException, ClassNotFoundException {
int numAbbrevs = in.readLength();
for(int i = 0; i < numAbbrevs; ++i) {
int abbrev = in.readLength();
Object o;
if (abbrev > at.getCapacity()) {
o = this.readObject(in);
at.getAbbrev(o);
this.abbrevs.push(o);
} else {
o = at.getValue(abbrev);
this.abbrevs.push(o);
}
}
}
首先讀取msgAbbrev的數量。然后再讀取length,如果length大於本次T3請求中存放abbrev的容量,則讀取對象,否則讀取值。而本次T3請求的abbrev的容量,就是由前面協議協商的AS標識的值,默認為255。
下面我們看一下簡化后的readLength代碼。這里我用python重寫一遍,反編譯的代碼可讀性很差,相關代碼在
weblogic.utils.io.ChunkedDataInputStream#readLength
而客戶端中,如果需要寫入對象,則直接寫入256,也就是FE01。
服務端調用readObject的代碼如下
而一般來說,在協商T3協議部分中,交換的信息有限,所以這部分將會weblogic.rjvm.JVMID weblogic.rjvm.ClassTableEntry weblogic.rjvm.ImmutableServiceContext
組成。
這里很好地解釋了網上流傳的內容
T3 Context處理
讀取完上面的內容后,開始讀取context
這部分主要內容是在例如rebind,lookup請求中,恢復請求上下文。T3協議是不分rebind等請求的,在最終處理階段由調用方法,也就是context去區分。
T3 協議內容處理
T3 協議在建立之初交換彼此的詳細信息,這部分被稱為PeerInfo。t3協議內容沒有具體定義,由被調用的方法參數類型決定
CMD_IDENTIFY_REQUEST
這個就是交換信息時的cmd頭。也就是1,響應則為2。
具體處理代碼在
weblogic.rjvm.ConnectionManagerServer#handleIdentifyRequest
協議體的具體內容如下
從這里可以看出,其實在每條T3流中,ACED
標識前面也是反序列化流,只不過修改起來比較復雜罷了。
CMD_REQUEST
客戶端在執行rebind,lookup操作以及調用實例等操作,cmd都為CMD_REQUEST。服務端只能通過invokeadbleId去區分客戶端的具體操作。具體代碼在weblogic.rjvm.RJVMImpl#dispatchRequest中
rid可用的列表如下
最終將請求包裝為線程調用
rmi指令
這種指令用來綁定,查找實例,這種操作的invokeableId為9。不要問我為什么,weblogic 就只這樣設計的
客戶端的代碼
服務端最終調用weblogic.jndi.internal.RootNamingNode_WLSkel#invoke(int, weblogic.rmi.spi.InboundRequest, weblogic.rmi.spi.OutboundResponse, java.lang.Object)
去處理每個具體的請求。在這里每一個case都對應rmi的一種操作。例如lookup對應16
這里將會讀取t3協議體內容,處理完成后返回。
lookup操作,最終返回這個實例所代表的rid
實例操作
回到rid處理部分,在這里查找到rid所代表的實例處理后,分發並處理。例如我現在通過weblogic的反序列化漏洞安裝一個實例后,通過lookup查找,該實例的rid為295。處理程序如下
CMD_ONE_WAY
這種請求與上面類似,只不過不需要向客戶端顯示執行后的結果。
T3內存馬的構想
前面我們說過,目前T3協議的反序列化攻擊回顯大多數都是通過反序列化漏洞綁定一個實例。例如我的攻擊程序代碼如下
綁定完成后,客戶端再通過lookup查找該實例去調用,實現T3的后門。但是這種攻擊方式有一個弊端在於很容易被發現,管理員可以通過查看weblogic的jndi樹去判斷是否被植入基於t3協議的后門。
當然,分析一遍T3協議的處理方式,有助於我們發現新的樂趣
T3的CMD_REQUEST請求,包裝為線程后,最終由weblogic.rmi.internal.BasicServerRef#handleRequest去處理
代碼如上所述,這里存在一個preInvoke,在權限校驗之前。這個東西類似於java web的Filter功能。
在這里我們只需要向commonInterceptors插入一個自定義的攔截器,即可實現t3版本的內存馬。
這種方法的缺點: 需要實現一個T3協議,不過好在了解協議后,重寫一個就十分方便了。
后面將分享一下python模擬調用T3的相關代碼