2018年,vbscript相关的漏洞异军突起,两个在野0day的APT攻击使得Vbscript相关的漏洞再次进入大众的视野,其中CVE-2018-8174这个发生在erase函数中的uaf漏洞更是分外出众。
该漏洞最有意思的地方不仅在于其在野0day的身份,其本身exp编写的利用方式也可作为vbscript uaf类漏洞利用的典范,其exp中大部分代码皆可复用,而这些都不是本文的重点,我想通过这篇文章来聊聊微软对于这个漏洞的修复历程,并以此谈谈一些漏洞相关的事情。
CVE-2018-8174这个漏洞的修复很有意思:
2018年5月9日:360第一次披露该在野漏洞,微软修复
2018年7月23日:360 Vulcan Team的古河披露了漏洞CVE-2018-8242,该漏洞和CVE-2018-8174发生在同一个函数erase,之前的补丁没有补全,微软再次修复。
2018年9月18日:google project-zero的ifratric又给出了一个erase内相关的poc,之后被微软命名为CVE-2018-8625,并再次修复。
可以看到该漏洞的修复一波三折,需要注意的是,这并不是三个发生在同一函数中不同机制的漏洞,相反这三个漏洞关系密切,关注过的朋友就应该知道,实际上三个漏洞在poc的构造上都大同小异,而微软在修复补丁上的疏忽却导致了之后连续两个 cve的诞生,废话不多说,我们来看看这三个漏洞的修复历程。
如下所示可以看到CVE-2018-8174相关erase函数在调用时的相关处理逻辑,其中最需要注意的是vbscript!VbsErase和oleaut32!_SafeArrayDestroyData,因为微软对于漏洞的相关修复都在这两个函数中。
由于相关漏洞中需要进行大量的array的操作,所以简单来看看array在内存中的结构,如下所示为erase调用时,栈中传入的array,array中包含一个obj对象,其中第一个绿色框标记了array对象,第二个绿色框标记了一个tagSAFEARRAY,第三个绿色框标记了array对应的元素对象,其类型结构如下所示:
typedef struct tagSAFEARRAY { USHORT cDims; //数组维度 USHORT fFeatures; ULONG cbElements; //一个元素所占字节 ULONG cLocks; PVOID pvData; //数据的Buffer SAFEARRAYBOUND rgsabound[1]; } SAFEARRAY, *LPSAFEARRAY; typedef struct tagSAFEARRAYBOUND { ULONG cElements; //元素个数 LONG lLbound; //索引的起始值 } SAFEARRAYBOUND, *LPSAFEARRAYBOUND; |
CVE-2018-8174
首先来看看CVE-2018-8174,如下所示,ClassVuln类中实现了Class_Terminate
函数。
- poc中创建一个ClassVuln的对象并赋值到ArrA array中
- 通过erase删除ArrA,从而导致ArrA(0)中的ClassVuln对象被删除,由于ClassVuln中定义了Class_Terminate,因此会进入到Class_Terminate中执行。
- Class_Terminate中将ArrA(0)赋值给ArrB,并返回,返回后erase正常清除ArrA中的元素,此时ClassVuln被删除。
- 但是ArrB中却保存了ClassVuln的引用,从而导致uaf
对于array中的每一个元素,通过VariantClear来进行清空。
如下所示对ArrA调用erase时ArrA的内存状态如下所示:
第二次对ArrB调用erase时,此时可以看到其中的ArrB(0)已经被删除,从而导致之后的奔溃。
如上所示,可以知道漏洞在于通过Erase释放一个array对象时,可以通过Class_Terminater回调函数将该对象中的元素进行复制,从而在Erase删除后获取一个悬挂指针,因此这个地方如果进行修复的话应该是保证在Erase函数中不能再对释放的数组元素中的内容进行操作,我们来看看微软的补丁,8174的补丁对函数VariantClear进行了以下修改,可以看到修改很简单,即当VariantClear处理对应的array对象的element时,将其类型设置为null,此时由于element类型被设置为null,进入到之后的回调函数时将无法获取到ArrA中的漏洞对象元素。
如下所示此时Erase 调用时ArrA的内存对象。
漏洞对象的类型由9被设置0.
往下执行vbscript引擎报错,因为ArrA(0)中的漏洞对象类型被设置为0,vbs将默认该位置的元素为空。
此时Erase ArrB,我们可以看实际上ArrB(0)并没有获取到对应的ArrA(0)引用。
CVE-2018-8242
实际上8174的补丁却并没有考虑周全,这就导致了古河发现了第二个漏洞CVE-2018-8242,在其相关的分析文章中提到,8174实际只是考虑了在每一个array元素进行释放前,将其type修改为null,这确实可以有效的防止攻击者在回调函数中读取array中的元素并赋值,从而在Erase函数结束后获取一个悬挂指针,但是却并不能防止以下两种情况:
- 在回调函数中如果再次释放array是否可能导致二次释放?
- 如果array中有两个元素,在VariantClear处理第二个元素ray(1)时,其中的元素触发回调是否可以往第一个元素array(0)进行写操作?
就以上的两个攻击面,当时古河都给出了相关poc,并证明都是可行的,可以看到这两个攻击面总结起来分别如下:
- array本身是否可以在回调函数中进行修改(但是这里古河只是给出了再次释放的例子,实际上读写也是应该考虑的,这就为第三个漏洞CVE-2018-8265埋下了种子)
- array中的元素在回调函数中是可以进行写入的
此处我们通过第一个攻击面二重释放来看看具体效果,poc如下,和之前8174差不多,只是在回调函数中再次调用erase将ArrA释放,当回调函数返回,会进行二次释放,从而导致崩溃。
进入Erase函数,此时的ArrA数组的内存对象如下所示:
回调函数中再次调用Erase。
此时可以看到ArrA(0)中的漏洞变量已经设置为null。
回调返回,进入到第一次Erase流程时由于对应的ArrA(0)已经被设置为null,之后的memset函数将直接导致崩溃。
此时我们来看看微软对该漏洞的补丁,补丁的位置在vbscript!VbsErase,此时从函数流程上来看可以发现微软已经将修补的执行流前置了,这是好事,可以看到
- VbsRrase在进入SafeArrayDestoryData前直接将将Safedescription设置为0
- 调用SafeArrayDestoryData清空ArrA中的元素
- Safedescription恢复
- 调用SafeArrayDestroyDescriptor,整个tagSAFEARRAY被设置为null
- Safedescription设置为0
由于在进入SafeArrayDestoryData前Safedescription被设置为0,这将导致在回调函数中再也不能对array中的元素进行任何的读写操作,同时回调函数中进行Erase操作时,由于会先判断Safedescription,因此也不能进入到SafeArrayDestoryData,从而造成二次释放。
对应的汇编补丁如下所示:
Erase函数调用,ArrA函数的内存如下所示:
对应Safedescription被保存,并置空。
如下所示,Safedescription被设置为0.
此时第二次调用Erase,获取的Safedescription为空,无法通过判断,从而不会进行之后的释放流程。
CVE-2018-8625
如之前攻击面所述,实际上之前的补丁只是将针对array中元素的操作,array对象释放的操作进行了遏制,但是对于array对象本身的读写操作,确实可以进行的,这就是2018年9月18日google projectzero报的第三个相关漏洞。
poc如下所示可以看到依然和8174基本一致,只是在回调函数中将array对象直接赋值成了class1对象,注意这里是针对array对象本身,而不是array对象中的元素。
Safedescription被设置为0.进入函数SafeArrayDestroyData
SafeArrayDestroyData返回,此时可以看array object的位置直接被修改了一个对象(8242中的补丁只是确保Safedescription为0来防止之后的读写,但是却并没有保障array对象本身是否能被修改),回调中直接将a这个之前的array对象修改为了一个Class1对象。
此时当SafeArrayDestroyData返回,对应的
- Safedescription恢复
- 调用SafeArrayDestroyDescriptor,整个tagSAFEARRAY被设置为null
- Safedescription设置为0
最终Safedescription设置为0,这将导致class1对象对应的位置被设置为null,从而造成之后的null地址访问崩溃。
为了彻底杜绝Erase中的漏洞,微软对vbscript!VbsErase再次进行了较大的改动。首先在新版本中进入SafeArrayDestroyData前,保存array对象的16个字节的内存,并将array的type类型设置为0,并在SafeArrayDestroyData返回后判断对应的array对象的type类型是否被修改,因为凡是在回调中直接尝试对array对象本身进行的修改操作肯定会将之前的type 0类型的字段修改,因此该判断能防止任何针对该对象的写操作,同时由于array对象本身type类型被修改为0,应此不能对该a进行相关的读取操作(危害类似8174),在判断完类型是否修改后,并没有直接信任内存中的array对象,而是将之前保存的备份内存进行了还原。
Erase函数调用,此时的a对象内存。
修改a对象的type类型,注意并没有将Safedescription置null。
此时SafeArrayDestroyData返回,可以看到array对象内存数据已经被修改为class1对象。
判断对应的type类型标记是否被修改,如果被修改则设置flag位。
将备份数据中Safedescription字段设置为0,并通过备份数据还原之前的array对象内存。
检测type修改的flag标记位,由于对应的type已经被修改。
直接弹窗报错。
错误如下所示:
此时对比之前的那张流程图即可看到微软对Erase中相关这个漏洞的三个尝试修改。
总结
最终的VbsErase如下:
- 备份array对象的16个字节
- 在进入SafeArrayDestoryData前直接array对象中的type字段设置为0(CVE-2018-8174中通过在VariantClear中将array对象中的元素类型设置为null,只能防止对array中元素的非法读取,CVE-2018-8242将array对象中Safedescription设置为0,可以有效的防止对array中元素的修改及array对象释放,但是并不能防止对array对象本身的修改)
- 调用SafeArrayDestoryData清空ArrA中的元素,被设置为null,pvData被设置为0
- 判断type标记是否被修改并设置标记位
- 调用SafeArrayDestroyDescriptor,整个tagSAFEARRAY被设置为null
- 备份中的Safedescription被再次设置为0
- array对象备份写回
可以看到实际上无论是CVE-2018-8174还是CVE-2018-8242,其实瞄准的都是对象中的元素(CVE-2018-8242二次释放的类型除外),直到两个0day连续拳把微软打蒙了,才直接在删除操作前直接将对象中的Safedescription指针直接置空,但是CVE-2018-8625漏洞却并不是出自对象中的元素,而是对象本身。
利用CVE-2018-8242和CVE-2018-8625有所区别,但却有着千丝万缕的联系,CVE-2018-8242主要是在通过array对象中的elment来实现对对象引用的递增,而CVE-2018-8625则是直接通过array对象本身来实现对象引用的递增,即a与a(0)的区别,但是本质上却都是引用计数的问题,两个漏洞不过是从array对象元素到array对象本身的一次升级。
由此可以看到8174->8242->8625的以此升级,但本质上都是array中的Erase删除操作
最早是array对象回调可读取array对象中的元素,到array对象回调可写入array对象中的元素,最终到array对象回调可修改array对象本身,而微软在对Erase函数漏洞的补丁修补上却每次都只考虑删除对象的最小校验集,从而导致每次都只能修复其中的某一方面,最终导致了另外两个cve的产生。
2018年自己的工作基本往漏洞分析利用,及在野0day发现上转移,其中也发现了flash 0day相关的在野攻击,感谢团队各位给予帮助的小伙伴(当然还有小笨蛋),去年最遗憾的是没抓到一个vbs的0day,而在这里作为漏洞研究者其实自己是应该检讨的,之前CVE-2018-8174分析得很早,在vbs这块也投入较多,却遗漏了微软在修复补丁后多次留下的攻击面。
分析漏洞到底应该做到何种地步,我想不仅仅是对漏洞本身成因,利用原理的理解,更应该是以点破面扩展攻击面眼见的过程,不断重复上述训练,直至相信:挖洞只是体力消耗,而非能力问题(这里无耻借用泉哥的原话)。
最后一首石岩先生的永夜送给各位:
https://music.163.com/playlist?id=38136682&userid=44168812
致我逝去的2018,声声慢慢,2019。。。。。。
参考链接
https://blog.csdn.net/qq_32400847/article/details/77799191
http://blogs.360.cn/post/from-a-patched-itw-0day-to-remote-code-execution-part-i-from-patch-to-new-0day.html
https://googleprojectzero.blogspot.com/2018/12/on-vbscript.html
https://bugs.chromium.org/p/project-zero/issues/detail?id=1668
https://mp.weixin.qq.com/s/WrSZpqgq6gvZwEIqghqggg
https://music.163.com/playlist?id=38136682&userid=44168812
转载请注明出处