前言

想要搞明白weblogicT3协议中的反序列化和后续的反序列化绕过,就得先需要了解weblogic如何处理T3协议中的反序列化数据,了解流程之后再去分析CVE-2015-4852、CVE-2016-0638、CVE-2016-3510这些漏洞,会事半功倍。

weblogic处理反序列化的流程

首先我们看一张流程图,这是weblogic处理反序列化数据的时候的函数调用图
在这里插入图片描述

readObject()
			readObject0()
				readOrdinaryObject()
					readClassDesc()
						readNonProxyDesc()		resolveClass()				Class.forName()
						readProxyDesc()			resolveProxyClass()			Proxy.getProxyClass()

截图比较粗糙,其起始位置是我们熟悉的ObjectInputStream类中的readObject方法。下面是一层层调用的函数,在红色框中的resolveClass函数我们很熟悉了,在上篇文章说过,就是weblogic反序列化时重写了这个resolveClass方法,由于只是继承了父类的resolveClass方法,造成了CVE-2015-4852漏洞。

好了我们接着说数据流向:
weblogic获得(7001端口)t3协议的反序列化数据,经过一个个函数的处理来到readClassDesc函数,此时有两个流向(其实不止,下面会讲到),两个分支最后都会获取反序列化数据的类对象。那么这个readClassDesc是什么呢?

readClassDesc是什么

先来看一看readClassDesc函数
在这里插入图片描述
上面绿色的官方解释是:读入并返回(可能为 null)类描述符。 将 passHandle 设置为类描述符的分配句柄。 如果类描述符无法解析为本地 VM 中的类,则 ClassNotFoundException 与类描述符的句柄相关联。此翻译来自Google翻译,哈哈。

官方注释比较生涩,我们知道Java序列化的时候,java序列化数据在流量传输,并不是随随便便杂乱无章的,序列化数据的格式是要遵循序列化流协议。

序列化流协议定义了字节流中传输的对象的基本结构。(包括类,字段,写入的数据等)
字节流中对象的表示可以用一定的语法格式来描述。

比如说在字节流中传递的序列化数据中,字符串有字符串类型的特定格式、对象有对象类型的特定格式、类结构有着类结构。而TC_STRING、TC_OBJECT、TC_CLASSDESC则是他们的描述符,他们标识了接下来这段字节流中的数据是什么类型格式的。

这样我们就很容易明白上图中的switch语句中各个case后面字段的意思了,通过字节流中的描述符来确定传递的数据类型,并交给对应方法去处理。

上面说是数据到readClassDesc函数之后,会有两个流向,为什么是两个呢?为什么上面说其实不止两个 还有很多其他的流向呢?

Question1不止两个还有很多其他的流向
从我们截图的函数可以看出,switch语句中有标识符的有四个,这我很明显的回答了问题。

TC_NULL描述符表示空对象引用
TC_REFERENCE描述符表示引用已写入流的对象
TC_PROXYCLASSDESC是新的代理类描述符
TC_CLASSDESC是新的类描述符

Question2为什么只有两个流向
weblogic是通过序列化我们发送的恶意类,才造成的漏洞,所以我们重点关注标识符是有关类的描述符,所以就只有两个流向。

跟进readNonProxyDesc函数:
在这里插入图片描述
在这里插入图片描述
readNonPorxyDesc函数主要做了什么呢?
1:readClassDescriptor()从流量中获取序列化类的ObjectStreamClass并赋值给readDesc变量
2:把readDesc变量当参数传入resolveClass函数,结果赋值cl变量(获取反序列化数据类名)
3:把ObjectStreamClass流(readDesc)和Class类对象传入initNonProxy方法,初始化并赋给desc.
4:将ObjectStreamClass类型的desc变量返回给readNonProxyDesc函数。

在这里插入图片描述
readNonProxyDesc将会传递给readClassDesc,进而传递给readOrdinaryObject函数。

接着就是核心部分 readOrdinaryObject函数:
在这里插入图片描述
这是部分截图,weblogic序列化的关键在于readOrdinaryObject尝试调用类对象中的readObject、readResolve、readExternal等方法。这些方法在上图中都已截图,感兴趣的可自行跟进。
下面看一张readOrdinaryObject调用各个函数的流程图:
在这里插入图片描述
在Weblogic从流量中的序列化类字节段通过readClassDesc-readNonProxyDesc-resolveClass获取到普通类序列化数据的类对象后,程序依次尝试调用类对象中的readObject、readResolve、readExternal等方法。

在这里提前透露下,CVE-2015-4852、CVE-2016-0638、CVE-2016-3510这三个漏洞,所利用的恰好依次是恶意类”sun.reflect.annotation.AnnotationInvocationHandler”中的readObject、”weblogic.jms.common.StreamMessageImpl”中的readExternal、以及”weblogic.corba.utils.MarshalledObject”中的readResolve方法

试想一下这个场景:在没有任何防护或防护不当的时候,攻击者通过流量中传入恶意类的序列化数据,weblogic将流量中的序列化数据还原为其对应的Class对象,并尝试执行恶意类中的readObject、readResolve、readExternal等方法。这就是CVE-2015-4852、CVE-2016-0638、CVE-2016-3510漏洞的核心。

Weblogic CVE2016-0638

前言

上篇文章我们说了weblogic CVE-2015-4852的成因,因为weblogic在接收反序列化数据时,重写的resolveClass直接调用父类,未做任何过滤才导致漏洞。今天我们来看一下CVE2016-0638,CVE2016-0638这个漏洞本质是对上个漏洞补丁的绕过。

补丁

weblogic的补丁都是购买了正版的服务才能在Oracle官网下载,不过再网上找到了p20780171_1036_Generic和p22248372_1036012_Generic这两个补丁包,这里是链接,打补丁的步骤也是比较容易,根据这篇文章下一步下一步就行。

docker cp ./p20780171_1036_Generic  weblogic1036jdk7u21:/p20780171_1036_Generic
docker cp ./p22248372_1036012_Generic  weblogic1036jdk7u21:/p22248372_1036012_Generic
docker exec -it weblogic1036jdk7u21 /bin/bash
cd /u01/app/oracle/middleware/utils/bsu
mkdir cache_dir
vi bsu.sh   编辑MEM_ARGS参数为1024
cp /p20780171_1036_Generic/* cache_dir/
./bsu.sh -install -patch_download_dir=/u01/app/oracle/middleware/utils/bsu/cache_dir/ -patchlist=EJUW -prod_dir=/u01/app/oracle/middleware/wlserver/
cp /p22248372_1036012_Generic/* cache_dir/
./bsu.sh -install -patch_download_dir=/u01/app/oracle/middleware/utils/bsu/cache_dir/ -patchlist=ZLNA  -prod_dir=/u01/app/oracle/middleware/wlserver/ –verbose

重启weblogic
cd /u01/app/oracle/Domains/ExampleSilentWTDomain/bin/
./stopWebLogic.sh
这时docker的镜像会停止
docker ps -a 查看刚才docker的ID
docker restart <CONTAINER ID>

接着把依赖包,代码,拷出来添加到idea的依赖中,这些步骤上篇文章有详细的过程,具体参考上篇文章
打上补丁之后,补丁的位置位于:

weblogic.rjvm.InboundMsgAbbrev.class#ServerChannelInputStream
weblogic.rjvm.MsgAbbrevInputStream.class
weblogic.iiop.Utils.class

weblogic.rjvm.InboundMsgAbbrev.class#ServerChannelInputStream
在这里插入图片描述
打了补丁后,代码中会在classname中进行一个类名单的检测。跟进我们看一下。
ClassFilter.isBlackListed()为静态调用,我们先看一下静态代码块:
在这里插入图片描述
在这里插入图片描述
在debug调试的时候发现 debug不到静态代码块,后来查了一下,java静态调用的时候,静态代码块会优先执行,也就是说在虚拟机注册的时候,静态代码块已经执行了,嗯,就是这样。
两个判断为true的话才会进入到updateBlackList函数 去更新黑名单,相当于注册黑名单。
在这里插入图片描述
在这里插入图片描述
现在黑名单已经有了,然后传入的类就会过这个函数,看他是否在这个黑名单里,跟进isBlackListed函数:
在这里插入图片描述
很容易看明白,存在黑名单返回true,不存在就返回pkgName的类名长度和类名。
既然是绕过黑名单肯定是找一个黑名单中没有的类,我们找到了weblogic.jms.common.StreamMessageImpl这个类,为是那么是这个类呢,上面分析了weblogic序列化的关键在于readOrdinaryObject尝试调用类对象中的readObject、readResolve、readExternal等方法。而StreamMessageImpl类的readExternal方法中正好是我们可利用的点。
在这里插入图片描述
StreamMessageImpl类的readExternal方法在执行时,864行去序列化 接收序列化数据的readObject方法。

具体整个流程为:
在这里插入图片描述

Weblogic CVE-2016-3510

这个cve漏洞其实和上一个思路基本一直,都是黑名单绕过,而CVE-2016-3510用的是weblogic.corba.utils.MarshalledObject
首先看这个类的构造函数:
在这里插入图片描述
构造函数接收object类型的参数,序列化后转换为字节数组赋给this.objBytes。
再看一下readResolve方法:
在这里插入图片描述readResolve方法会将this.objBytes反序列化,执行readObject。this.objBytes是我们可控的,由MarshalledObject构造方法中传入的var参数控制。
整体流程如下:
在这里插入图片描述

参考:
https://github.com/zhzhdoai/Weblogic_Vuln
https://www.cnblogs.com/nice0e3/p/14207435.html
http://redteam.today/2020/03/25/weblogic%E5%8E%86%E5%8F%B2T3%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%8F%8A%E8%A1%A5%E4%B8%81%E6%A2%B3%E7%90%86/#CVE-2016-0638
https://xz.aliyun.com/t/8443
https://mp.weixin.qq.com/s?__biz=MzU5NDgxODU1MQ==&mid=2247485058&idx=1&sn=d22b310acf703a32d938a7087c8e8704

posted on 2022-09-13 18:13  noone52  阅读(150)  评论(0编辑  收藏  举报