1

CVE-2021-40444-Microsoft MSHTML 远程命令执行漏洞分析(三)

 1 year ago
source link: https://paper.seebug.org/1806/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

CVE-2021-40444-Microsoft MSHTML 远程命令执行漏洞分析(三)

53分钟之前2022年06月09日漏洞分析

作者:且听安全
原文链接:https://mp.weixin.qq.com/s/hHlscdLIvO0BY173ksq8vA

第一部分:样本分析

CVE-2021-40444-Microsoft MSHTML远程命令执行漏洞分析(一)

第二部分:漏洞复现

CVE-2021-40444-Microsoft MSHTML远程命令执行漏洞分析(二)

本系列第三篇主要对漏洞成因和原理做个分析。漏洞成因分析可以说即是技术活又是体力活,整个分析过程会比较冗长,这里我们不会像其他分析文章那样直接给出结论(让人摸不着头脑),而是会按照我的分析思路一步一步往前走,那么下面开始。

既然我们基本知道了漏洞样本的执行流程,那么能想到就是挂上调试器在感兴趣的地方下断点,第一个断点尝试下在kernelbase!CreateFileW,找到创建championship.inf文件的地方(由于调用CreateFileW的地方会比较多,这里建议可以设置个条件断点,比如判断文件名参数poi(esp+4)+xx地方的值是0x6e0066,即文件后缀为".inf"):

图片

看一下函数调用堆栈,可以看到,大部分处理都是在urlmon这个模块中:

图片

下面可以通过IDA进行简单的静态分析,可以找到在GetSupportedInstallScopesFromFile这个函数中开始对cab包进行处理:

图片

先是通过GetExtnAndBaseFileName判断传入文件的后缀名,如果是.cab则返回2(即可以通过上面的判断,进入下一步处理),其实这里只判断了3个字节,只要文件名后缀是.cabxx这样的都能通过验证,算是一个小bug:

图片

如果临时目录创建成功,则通过ExtractInfFile(如下图)来解压cab包中的inf类型的文件(注意,这个函数只会解压后缀是inf的文件),从IDA中可以看到调用GetExtnAndBaseFileName后需要返回值为5(参考前面的图,当后缀为inf时返回5),因此该函数只会解压后缀是inf的文件,最终调用ExtractOneFile来实现解压功能(注意,如果cab中有多个inf文件,则只会解压第一个遇到的):

图片

从上图可以看到,该函数先调用了一次Extract,如果成功了才会开始解压inf文件,我可以简单跟进去看一下这个Extract(其实后面还会再次调用Extract函数),内部主要的功能是顺序调用FDICreateFDICopyFDIDestroy,如果任何一个函数失败或异常则返回错误号,否则返回0:

图片

其中FDIDestroyFDICopy函数则直接调用cabinet中的FDIDestroyFDICopy,而FDICreate函数会动态加载cabinet.dll,并调用cabinet中的FDICreate函数来创建一个FDI对象:

图片

cabinet.dll我们先不跟进去,还是回到urlmon.dllExtractInfFile函数中,程序通过一个循环来遍历cab包中的文件,如果发现后缀为inf的文件,则会调用ExtractOneFile函数来解压inf文件,并退出函数:

图片

ExtractOneFile函数其实也很简单,还是通过Extract函数来实现解压inf文件的功能,但是跟前面第一次Extract不同的是传入的参数pfnfdin存在不同。

图片

通过调试可以看到,传入Extract的参数pfnfdin包含了需要解压的inf的名称 ../championship.inf

图片
图片

现在我们再次进入这个Extract函数,跟进cabinet模块中的FDICopy看看,下图是该函数的定义:

图片

FDICopy函数处理过程其实还是会回调urlmon提供的回调函数来实现cab的解压:openfuncreadfunc等等(这些回调函数是在FDICreate的时候初始化的):

图片

图片

我重点关注的是CreateFileW创建inf文件的地方,cabinet模块回调了urlmon中的fdiNotifyExtract函数:

图片

现在进入fdiNotifyExtract函数,可以看到前面分别调用了catDirAndFileAddFileNeedFile,如果都能成功则最后调用Win32Open来创建inf文件(终于找到关键点了):

图片
  • catDirAndFile函数:将解压文件夹路径和文件名被拼接在一起(注意:该函数并没有对"../"这样的字符串进行过滤,导致目录穿透)。
  • AddFile函数:文件信息加入FDI结构体中(对漏洞触发没有什么影响)。
  • NeedFile函数:判断是否需要该文件(注意:如果验证不通过将不会调用win32Open)。
  • Win32Open函数:直接调用CreateFileA创建文件。

先来看catDirAndFile函数,主要是会调用PathCchCanonicalizeA对路径进行一系列的处理(流程比较复杂,看着头晕,有兴趣的可以自己去逆一下,说不定能找到什么别的问题),幸运的是并不会对"../"进行过滤:

图片

NeedFile功能比较简单,主要是判断文件名是否一致,还有一个关键点是hfdi+808这个地方必须有数据(在ExtractOneFile函数中,该位置被正常赋值了,因此能够通过验证):

图片

最后顺利到达Win32Open函数,传入的路径就是C:\Users\user\AppData\Local\Temp\Cab82BF\../championship.inf

图片

到这还没结束,大家可能还记得,我们在复现漏洞的时候发现释放的inf文件很快就会被删除,导致后面无法实现代码执行,我们继续往后走来一探究竟。创建完inf文件后,我们回到urlmon中的GetSupportedInstallScopesFromFile函数,它会立即调用DeleteExtractedFiles函数来删除解压出来的文件:

图片

但是我们用调试器跟入DeleteExtractedFiles后发现并没实际调用DeleteFileA,因为file_info[2]中的值非0,所以没有删除inf文件。

图片

我们通过内存断点看一下这个file_info[2]到底是在哪里赋值的,通过断点我们发现该位置是在urlmon!AddFile函数被赋值为1的,然而后面直到DeleteExtractedFiles函数,该位置始终没有被清空,因此inf文件得以保留:

图片

到底是怎么回事呢,直接调试分析比较麻烦。根据前面的测试,正常的cab也会将inf文件释放到Temp目录,但是会马上被删除。因此我们可以用一个正常的cab包再调试一遍看一下,找到该值被清零的位置。很快我们便找到了赋值点:正常流程下fdiNotifyExtract函数会调用MarkExtracted来清空该值。

图片

fdiNotifyExtract函数中可以看到,当调用参数type为fdintCLOSE_FILE_INFO(值为3)时,才可能调用MarkExtracted

图片

调用fdiNotifyExtract函数父函数是Cabinet!FDIGetFile,我们仔细看一下FDIGetFile中的处理流程(如下图),就是循环调用FDIGetDataBlock来读取inf文件的数据,并调用urlmon!writefunc函数将数据逐块写入文件。这里如果文件大小正常,则最后一次循环后将转跳至LABEL_13调用fdiNotifyExtract,且type就是fdintCLOSE_FILE_INFO,因此文件会被删除。由于攻击者伪造了文件大小,导致remain_size超出了文件的正常大小,最终导致FDIGetDataBlock异常并返回0,后面就转跳至LABEL_20调用urlmon!closefunc并返回了:

图片

现在我们基本理清了inf文件被创建的整个流程,最后我们总结一下:

  • mshtml.dll 在处理<object codebase="http://xx.xx.xx/xxx.cab">对象时,会利用urlmon.dll来实现cab安装包的下载
  • urlmon.dll模块会调用urlmon!SetInstallScopeFromFile来设置安装文件,该文件后缀必须为".cab"
  • 接着调用urlmon!ExtractInfFile来释放cab文件中的一个inf文件,该文件后缀必须为".inf"
  • 【目录穿透漏洞】:由于inf文件名以"../"开头,拼接的文件路径其实在上一层目录
  • 最后调用urlmon!Win32Open实现在Temp目录中创建inf文件
  • 【文件驻留漏洞】:文件大小异常使标记位没有被正常清零,导致inf文件没有被删除

至此,漏洞原理分析就告一段落了,整个漏洞分析过程还是很有意思的,下一篇计划做个补丁分析。


Paper 本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1806/


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK