前言
本文主要分析Weblogic的反序列化漏洞,在weblogic里面其实反序列化漏洞利用中大致可以分为两种,一种是基于T3协议的反序列化漏洞,一种是基于XML的反序列化漏洞。
基于T3协议漏洞: CVE-2015-4582、CVE-2016-0638、CVE-2016-3510、CVE-2018-2628、CVE-2020-2555、CVE-2020-2883
基于XML:CVE-2017-3506、CVE-2017-10271、CVE-2019-2729
T3协议
简介
T3协议是一种基于Java远程方法调用(RMI)实现的协议,它在WebLogic Server中用于实现客户端和服务器之间的通信。其主要功能是使用Java对象进行通信,能够传输包括Java对象在内的各种数据类型,实现WebLogic Server集群中不同服务器之间的通信和数据传输,以便实现负载均衡等功能。与传统的RMI通信协议相比,它提供了一些额外的特性,如服务端可以持续追踪监控客户端是否存活的心跳机制,以及通过建立一次连接完成全部数据包传输,从而优化了数据包大小和网络消耗。此外,T3协议还包含了请求头和请求主体两部分,这些部分分为七个部分,其中第一部分是协议头,后面的2-7都是请求主题,这有助于更好地理解和管理通信过程。
T3协议结构
头部
首先,T3协议包含请求包头和请求的主体这两部分内容。这意味着在数据包发送之前,需要先发送一个头部(header),其中包括请求的ID、长度等信息。例如,客户端首先发送一个T3数据包的头部,如t3 12.2.1 AS:255 HL:19 MS:10000000 PU:t3://us-l-breens:7001
,其中12.2.1
表示本地客户端请求,AS
是对象的大小,HL
是header的长度,PU
指定目标。
简单写个socket发包脚本,wireshark选择any追踪流分析一下
1 | import socket |
在这个请求包中,还包含了一个HELO响应,这通常是服务器向客户端返回的第一个消息。在这个响应中,服务器给出了自己的版本号,即10.3.6.0.false ,并指定了另一个端口号2048。
请求主体
我们发送的请求主体,可以被划分为七部分内容,至于这7个部分有什么区别联系,可以参考下面两张图
第一个非 Java 序列化数据,也就是我们的请求头:t3 12.2.3\nAS:255\nHL:19\nMS:10000000\n\n
剩下的就是主体部分。不难看出,每一部分序列化数据前缀都是ac ed 00 05
,通过这个特征,我们就可以把这些部分做出一个合理的区分。而我们的poc构造,其关键就在于将恶意序列化数据替换掉2-7这几个部分里的序列化数据,形象一点地表示就是下图这样 (只需要替换掉其中一个就行)
当然,只保留一部分的序列化数据也是合理的,下面这种利用方式也同样可以奏效
CVE-2015-4852
挺经典的洞,这里也是跟着研究一下,准备好的poc如下,打的CC6
1 | import socket |
执行结果如下
wireshark抓包分析一下,发现ysoserial的恶意payload已经插进去了
- 数据包长度
- T3协议头
- 反序列化标志:T3协议中每个反序列化数据包前面都带有fe 01 00 00,再加上反序列化标志ac ed 00 05就变成了fe 01 00 00 ac ed 00 05
- 序列化数据
远程调试环境搭建
远程环境是docker起的,先把这几个依赖给拿出来
1 | docker cp weblogic1036jdk8u202:/u01/app/oracle/middleware/modules `pwd` |
idea在项目结构里把这几个东西加到库里边导入依赖
在docker里运行如下命令,若出现如下界面,则代表启动正常
1 | sh /u01/app/oracle/Domains/ExampleSilentWTDomain/startWebLogic.sh |
idea里面做如下配置,端口和主机都要对应上远程环境
在InboundMsgAbbrev#readObject
处打上断点,同时打poc
调试环境搭建成功
漏洞分析
接着上面的步骤来分析,先看到45行ServerChannelInputStream
这块,此时var1封装着未被解析的序列化数据,跟进此处的new ServerChannelInputStream(var1)
ServerChannelInputStream
是个继承于ObjectInputStream
的内部类,并且重写了resolveClass
方法。这里resolveClass
先打个断点就不管它了,跟进getServerChannel()
看看。
返回了一个ServerChannel
对象,此处的connection
包含了一堆相关的信息。该方法的意义就在于处理我们的T3协议所传递的信息,没有必要管他,直接步过,。
回头看看我们这里返回的对象,其实就是解析出了一堆socket信息,整理了下信息流,在得到信息流之后再进行readObject
因为这里的readObject
并没有进行重写,所以本质还是ObjectInputStream
的那一套,唯一不同的地方就在于resolveClass
是被重写过的,当ObjectInputStream
调到resolveClass
的时候,其实用的是ServerChannel
重写过的 resolveClass
。中间那套原生反序列化的分析步骤就先越过了,直接看ServerChannel#resovleClass
这个重写方法。
大体看了一下源码,发现和原生反序列化实现的效果没什么区别,黑名单之类的防护措施都没有做,故而造成反序列化漏洞,其大体流程可按下图总结。
CVE-2016-0638(修复绕过)
环境搭建
因为CVE-2016-0638其实就是CVE-2015-4852的修复绕过,我们只需要在原有环境基础上把补丁打好就行。如图所示,我们先把补丁复制到docker里面去
之后依次键入以下命令
1 | cd /u01/app/oracle/middleware/utils/bsu |
最后两条命令运行时间可能稍长点,返回以下界面即代表运行无误
搞完之后重启一下docker,发现原来的poc已经打不通了,证明修复成功
之后就和之前一样了,把依赖包拉到项目里面分析即可
漏洞分析
毕竟本质是个补丁的bypass,我们先看下原有的补丁是怎么打的
简单粗暴地在resolveClass
加了个黑,相关名单如下所示
1 | static final String BLACK_LIST_PROPERTY = "weblogic.rmi.blacklist"; |
这里的bypass是一个二次反序列化的实战案例,用到了StreamMessageImpl
这个类。我们先简单看一下这个类的关键源码。
在readExternal
里面对var5进行了readObject
调用,并且这里的var5还是个ObjectInputStream
对象,再加上readExternal
本质上和readObject
处于同等触发难度,妥妥的二次反序列化节点,接下来我们分析一下payload,这里用到的生成器是weblogic_cmd
,下载地址如下 https://github.com/5up3rc/weblogic_cmd/
Exp分析
项目导入idea后如下设置,之后开启debug模式
入口类149行处打上断点,开始debug调试分析
先将我们的参数解析为cmdline,之后再根据解析得到的参数的值对host
,port
等变量进行赋值,最后进入executeBlind()
方法
从-C
参数解析取得要执行的命令,这里要注意一下必须同时存在-B
和-C
参数才能接着往下执行,我们跟进62行的blindExecute()
对os进行判断,以此决定执行命令的终端,之后在43行处得到反序列化payload,跟进
先跟进里面的blindExecutePayloadTransformerChain(execArgs)
看一眼,发现是链式反应里的Transformers
数组设置,接着再回到serialData
这里
发现原来是CC1的gadget
,很烦,和远程环境8u202对不上,得先自己改成CC6的链子,这里暂停调试一下把CC6的链子贴上去改改,调完之后正式跟进selectBypass()
1 | ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); |
因为Main.Type
设置的是streamMessageImpl
,所以这里跟进的是38行的逻辑
给套了层streamMessageImpl
的壳,到此为止payload即生成完毕
之后就是把payload按T3协议发送过去了,这块就不跟了,和上面的python本质是一样的
执行结果如下
CVE-2016-3510(修复绕过)
在Weblogic从流量中的序列化类字节段通过readClassDesc-readNonProxyDesc-resolveClass获取到普通类序列化数据的类对象后,程序依次尝试调用类对象中的readObject、readResolve、readExternal等方法
根据上面这段话,这里还有一个几乎是一样原理的bypass,exp分析就不过了,和0638几乎一样,只是改了个Main.Type
的事儿,简单看一下漏洞分析就是了,我们来到MarshalledObject#readResolve
这块
49行一眼二次反序列化,接着看看this.objByte
怎么出来的就行了
实例化的时候就已经搞定了恶意序列化数据,over