原文链接:安全客

Author:k0pwn_ko

MS16-030漏洞

MS16-030漏洞是Windows OLE的远程代码执行漏洞,由于OLE没有正确的验证用户输入,导致通过构造特殊的文件或者程序可以触发此漏洞,导致用户点击后远程执行任意代码。

关于漏洞补丁信息:

https://support.microsoft.com/zh-cn/kb/3143136

关于漏洞说明文档:

https://support.microsoft.com/zh-cn/kb/3143136

MS16-030补丁对比分析

最近由于对我们一些相关产品的研究,需要分析MS16-030的补丁。在分析完之后,我们发现这个补丁中关于这个漏洞的攻击部分是很有趣的,而且把这个漏洞的一部分细节拿出来分享应该对大家都有所帮助。这篇文章主要就是重点分享从补丁对比,到触发MS16-030漏洞的过程。

OLEAUT32.dll是Windows OLE的一个重要动态链接库,我们可以看到在补丁前后对于OLEAUT32动态链接库中修改过的函数的列表。

查看这个函数列表,我们选择仔细阅读OLEAUT32!_ResToIcon这个函数功能,不过我们也需要考虑到这个过程可能会涉及到其他函数的变动。那么首先我们这个函数可能是用来执行一些和图标相关的一些功能,来看一下关于这个函数补丁前后的逻辑流程图的对比。

通过对这张流程图前后对比中可以发现,关于ResToIcon函数在逻辑执行流程上有很多变化,但是我们需要考虑每一个代码块执行的变化是否是由于存在安全漏洞才改变的,让我们重点来分析每一个代码块。

当分析到上面的代码块的时候,我们发现这个很有可能是我们触发MS16-030漏洞的关键部分之一,我们可以看到这里有两个调用,一个是call SizeTAdd,还有一个是call ULongLongToULong,在文档中我们可以查找一下这两个函数调用:

SizeTAdd -- 这个函数是OLEAUT32.dll的一组内联函数,作用是进行算术运算并且检验结果的有效性,优势是对性能影响较小。

ULongLongToULong -- 这个函数也是OLEAUT32.dll的一组内联函数,作用是进行类型转换并且检验结果的有效性,优势也是对性能影响较小。

这么看来这个位置有可能就是能触发MS16-030的关键位置,那么问题来了,如何能够让符号路径执行到这个分支?首先我们需要在OLEAUT32.dll中找到一个路径的入口点,这条路径可以执行到ResTICon这个函数。

对于符号路径的执行我们提供了一张流程图,正如所示,有几条路径都可以执行到ResTICon,通过一些试验,我们选择使用由vbscript.dll中调用的导入函数OLEAUT32!_OleLoadPicture(图中黄圈所示),通过这个函数再执行到ResTICon函数位置。

那么首先我们就需要分析一下vbscript.dll中的导入函数的执行路径,通过IDA pro打开vbscript,通过流程图我们可以看到导入函数OLEAUT32!_OleLoadPicture的执行过程,流程非常简单。

可以看到,在vbscript.dll中经过几次调用,会执行到impOleLoadPicture位置,这个imp实际就是在vbscript.dll中执行到该位置时会调用OLEAUT32.dll中的OleLoadPicture函数,这样就和之前我们分析的几种分支中我们选用的部分重合了。

这样看来,我们只要调用vbscript!VbsLoadPicture就有可能执行到我们需要的分支流程中,可以通过vbscript LoadPicture的API调用到这个vbscript函数,从而执行到OleLoadPicture函数分支。

PoC构造

我们尝试使用这种方法,首先在vbscript.dll中调用导入函数OleLoadPicture的位置下了断点,然后创建了一个图标,这个图标加载时会调用LoadPicture的这个API,也就是会调用VbsLoadPicture函数,打开这个图标,但是却没有命中断点。我们只能来检查一下这个过程为什么没有命中断点。

通过分析,我们发现在调用VbsLoadPicture之后,执行过程中会先调用一个call impStgIsStorageFile,这个函数也是OLEAUT32.dll中的函数,正是ole32!StgIsStorageFile导致的无法执行到OleLoadPicture,那么这个函数的是什么呢?

通过查看MSDN,我们只找到关于StgIsStorageFile函数很少的信息,这个函数主要用来表明特定磁盘中是否包含存储对象。

通过Google检索我们需要从VbsLoadPicture到OleLoadPicture的这个执行流程,其实这个过程是一个类似于COM结构化存储的执行过程,通过MSDN可以找到一些关于这个过程的信息。

“结构化存储就是将单个文件以数据和流的形式作为一种结构化集合来处理,从而在COM中保持数据和文件的持久性”。

这样看来,我们需要创建一个结构化的存储对象然后插入一个图片,来使函数执行到OleLoadPicture函数调用位置。那么这样的一个文件我们应该如何创建呢?

进一步通过Google搜索,我们找到了一款叫做OpenMCDF的工具,下载地址是: http://openmcdf.sourceforge.net 。这样我们要做的就是在存储化结构的根节点位置创建一个包含图片的流(译者话:这个存储化结构的概念在很多地方都有用到,比如Office文档,pdf文件等等),然后通过vbscript再次打开这个文件调用LoadPicture函数。我们使用OpenMCDF工具中一个结构化存储的界面。

通过这个界面创建文件之后,我们再次在vbscript.dll中的OleLoadPicture下断点,但是还是没有命中,这是什么原因呢?

经过分析,我们发现,在调用OleLoadPicture之前,还会经过另一处检查。

实际上我们执行的过程执行了粉色块的部分,而OleLoadPicture调用则在右边,可以看到在之前会调用ole32!CExposeDocFile::OpenStream,这个过程会有一个对流名称“CONTENTS”的检查,如果正确则会进入右边分支(我们想要的)处理,因此,我们需要在OpenMCDF中修改流的名称,改为CONTENTS。

这样修改之后,我们的文件打开之后,就能成功命中OleLoadPicture断点了。接下来我们通过Windbg附加这个任务来跟踪,同时在OLEAUT32.dll中的ResTIcon函数下断点,来确定OleLoadPicture之后是否能够命中我们分析到的漏洞触发的关键函数。

可以看到,我们确实命中了断点,到这里为止,我们就很接近之前分析到的发生改变的代码分支了,但是,实际上我们利用OpenMCDF生成的文件是一个正常的文件,也就是说其中包含的图标的这个流也是正常的,那么接下来,我们使用dumb fuzzer(译者注:dumb fuzz是一种fuzz技术,不管文件格式问题,完全采用生成全随机化的数据来进行fuzz)的方法来生成畸形文件,通过一个vbscript的脚本来执行文件,再通过eEye公司的一款产品来自动化调试。当然,我们也要通过Windbg中个gflags开启页堆监视(译者注:通过Windbg下的gflags.exe /I [process] +ust +hpa开启页堆监视)。

我们写了一个简单的dumb fuzzer的脚本。

接下来运行这个fuzz脚本,并且通过eEye公司的工具跟踪,大概在619次测试过后会eEye的工具会捕获到一次崩溃,利用Windbg加载这个崩溃文件。

到此,我们通过补丁复现了这个Windows OLE的漏洞,并且在打上补丁之后我们发现再用这个PoC无法触发漏洞了。

分析补丁、补丁对比是一个非常有趣的过程,但实际上使用bindiff(译者注:一款非常好用的补丁对比工具)这样的补丁对比工具可以大大加快我们的分析进度,而且有助于我们全面了解补丁前后动态链接库的变化情况,以便分析出触发漏洞的执行路径。同样,将文件类型转换成结构化存储去测试也有可能成为未来漏洞挖掘一个很有意思的方向。