一 、原理
(一)概述
接CVE-2015-4852,
Oracle使用黑名单进行了防护,如下,
看一下CVE-2015-4852调试过程中记录的信息,
触发时的调用栈,
结合上一次调试过程中记录的信息,我们可以看到,这个fix阻断了CVE-2015-4852的exp中在偏底层的位置对关键类的反序列化,可以说是打断了利用链。
但同时,我们也要注意到,对那几个关键类的反序列化的禁止仅存在于那三个列出的反序列化点。也就是说,如果能在别的反序列化点,通过适当的readObject对那几个关键类进行反序列化,也有可能能触发RCE之类的漏洞。这里和上一个漏洞类似,将反序列化点转移到了一个新类的内部。
实际上,CVE-2016-3510和CVE-2016-0638的攻击都是基于对黑名单的绕过。
(二)CVE-2016-3510
原理是将反序列化的对象封装进了weblogic.corba.utils.MarshalledObject,然后再对 MarshalledObject进行序列化。当字节流反序列化时 MarshalledObject 不在 WebLogic 黑名单里,可正常反序列化,在反序列化时 MarshalledObject对象调用 readObject 时对 MarshalledObject 封装的序列化对象再次反序列化,这样就逃过了黑名单的检查。
MarshalledObject比较符合需求,即在封装原链的基础上可以通过自身的反序列化来反序列化成员变量。
(三)原理
结合着调试分析一下工具的逻辑与流程,
首先按照CVE-2016-3510的实际情况,将TYPE改为marshall,
debug,
继续向前,可以看到此处的handler的类型是AnnotationInvocationHandler,
跟进此处的selectBypass,
可以看到,这里根据TYPE选择了marshall类型的包装,从这里我们可以猜测这个漏洞对于CVE-2015-4852的防护黑名单的绕过是基于额外封装的,黑名单限制了那三个反序列化点,不让它们对那几个关键类进行反序列化,于是又找了另一个包的类,进行了二次封装,通过反序列化这个marshall对象触发构造好的AnnotationInvocationHandler。这一点在下面漏洞的调试中我们可以有一个比较直观的感受。
接下来进行构造marshallObject,
可以看到,这里的marshallObject中的成员变量objBytes即为AnnotationInvocationHandler,
继续运行,进行反序列化,
可以看到这里反序列化封装的是MarshallObject类,其内部才是构造好的AnnotationInvocationHandler,
继续,就该发包了,
下面的内容在漏洞的调试环节展示。
Externalizable接口extends 了Serializable接口,而且在其基础上增加了两个方法:writeExternal()和readExternal()。这两个方法会在序列化和反序列化还原的过程中被自动调用,以便执行一些需要的操作。
结合上面所讲,原理部分主要是解释是怎么绕过黑名单(这里主要关注黑名单中ServerChannelInputStream)的,重点解释在反序列化时marshall引发了什么后果。
weblogic.corba.utils.MarshalledObject这个类没有实现readObject或者readExternal函数,所以在反序列化的时候采用ObjectInputStream的默认流程。但这个流程会调用辅助类ObjectStreamClass的invokeReadResolve函数,后者会调用MarshalledObject的readResolve函数,查看readResolve我们会发现,readResolve中有readObject的调用,而其参数正来自其本身的objBytes变量,如下:
个人对这个漏洞的理解是,所以在构造MarshalledObject时把生成的恶意payload Object作为参数传给构造函数即可。装填好、封装好(甚至只用一个Bytes而不是别的需要触发的数据类型)的MarshallObject可以正常走原触发点,因为MarshallObject不在黑名单之中,这一点将在漏洞调试部分展示。
二、调试
(一)环境搭建
与CVE-2016-0638相同,不再重复
(二)复现
配好Application,
run即可,
(三)调试
断点下在MarshallObject.class的readResolve,
结合调用栈,我们看看反序列化点上的情况,
可以看到,其实是走了那几个反序列化点的,
向栈底方向翻看几个函数,
最关键的是下面这个,
个人认为,这里是整个调试过程中的一个关键点。作为CVE-2015-4852中黑名单做保护的一个点,ServerChannelInputStream对一些关键类的反序列化做了限制,但此时var1是一个MarshallObject(或曰是一个Marshall外壳加AnnotationInvocationHandler的内里),并不在黑名单所限制的那几个关键的类里面,所以下面仍然可以正常的进行反序列化。
继续向下调试,跟进Marshall的readObject,尝试着跟了下,
在其中就进入了AnnotationInvocationHandler的invoke,
此时查看调用栈,
里面多是些java内部的函数,没有再跟进了,
接下来也就可以正常触发了原反序列化链,
接下来的过程就是CVE-2015-4852中的过程,不再重复。
三、收获与启示
CVE-2016-3510和CVE-2016-0638都是基于对CVE-2015-4852的黑名单的绕过。这个黑名单的防御思路是在InputStream中设置了一个Filter,检查InputStream中包含的类是否在其中。两者的思路有比较大的相似之处,即没有对原攻击链做太多的修改,只在黑名单里的反序列化点上做文章,合理的选择新的对象进行二次封装使反序列化点仍然可以触发。所以我认为,找这种漏洞的方法之一便是寻找一种类,这种类可以反序列化自身的成员变量。
通过这两个漏洞我们可以看到,黑名单防护是不那么靠谱的,尤其是像weblogic这样的复杂的一个程序,既有不在少数的引入库,又有比较复杂的自身结构。如果说CVE-2015-4852是有Commons Collections引发的,这2016的这两个主要是由Weblogic自身的问题引发的,只是之前没有被发现或提出而已。
这两个漏洞的原理和CVE-2015-4852差不多,在以后的学习生活中,我会尽可能的深入一点,多探究一点细节,加深自己对相关知识的理解。