CVE-2014-0301分析

By Zing - 2014-08-24

原文链接(翻译:Zing)

漏洞信息

发布日期:2014-03-11
更新日期:2014-03-13

受影响系统:
Microsoft Windows XP Professional
Microsoft Windows XP Home Edition
Microsoft Windows Vista
Microsoft Windows Server 2008
Microsoft Windows Server 2003
Microsoft Windows 8.1
Microsoft Windows 8
Microsoft Windows 7

产生原因:

DirectShow是微软公司在ActiveMovie和Video for Windows的基础上推出的新一代基于COM(Component Object Model)的流媒体处理开发包,与DirectX开发包一起发布。

Microsoft DirectShow在解析JPEG图形时存在安全漏洞,可被恶意利用造成内存破坏。

译文

这边文章将会演示怎样在安全补丁发行的过程中,利用厂商提供的公开的信息触发漏洞。分析的漏洞是CVE-2014-0301,补丁MS14-013.

分析目标

首先,我尽可能地收集了目标漏洞的信息。通过简单查看bulletin,可以快速知道什么文件被打补丁了。对于Windows 7(32位)系统,新文件包括 qedit.dll,文件修改日期为2013-06-03。
接下来,我做了一个旧版文件的备份,然后安装更新,在做一个新版本文件的备份。现在我就有打上补丁和未打补丁的qedit.dll文件了。

定位打上补丁的函数

在这个例子中,我关注的是Windows7版本的文件。要做的第一件事是比较文本文件的差异。我使用了DarunGrim3,这个工具可以自动比较二进制文件差异。下面是打补丁和未打补丁的差别,根据安全性评分分类(note:未打补丁的函数不在列表中,列表中只有对打补丁的函数打了补丁和未打补丁版本的比对)

DarunGrim3的安全性评分基于以下几个应用到任何代码中的启发式想法:

  • cmp/test指令的添加
  • 0xFFFFFFFF的任何位置的出现
  • 调用strlen()家族的成员,通常用来给缓冲区溢出打补丁
  • 调用StringCchCopy()家族,通常用来给缓冲区打补丁
  • 调用UlongLongToULong(),通常用来给整数溢出打补丁

下面就是比对文件的结果,根据安全性评分排名(未打补丁的函数不在列表中)

可以看到WindowsXP和Windows7都打了补丁的函数:

  • LoadJPEGImageNewBuffer
  • OpenTGAFile
  • OpenDIBFile
  • CGenStilVid

在IDA中搜索这些函数,可以看到函数定义:

LoadJPEGImageNewBuffer(unsigned short *, class CMediaType *, unsigned char * *)
OpenTGAFile(void *,uchar * *,CMediaType *,uchar *)
OpenDIBFile(void *,uchar * *,CMediaType *,uchar *)
CGenStilVid::~CGenStilVid(void)

~CGenStilVid() diff

第一个改变的函数看上去像一个析构函数,可能会很有趣,结果却发现除了文件偏移的变化之外,没有什么改变。它的安全性评分为0。因此现在排除这个函数是安全的。

LoadJPEGImageNewBuffer()diff

这个函数中大多数变化和比较有关,看上去是由编译器优化导致的。但是有一处变化很明显。在调用LoadJPEGImageNewBuffer()之后,函数继续执行删除指针的部分。多了一个新指令 mov [esi],ebx.在打补丁版本的函数中,ebx存放了值0,因此这个新指令简单的清除了esi存放的值。
第一眼看到时,认为这表示一个悬垂指针,因为这个补丁明确的清除对删除对象的引用,因此它不会被重用。未打补丁的CGenStilVid对象的中,它被删除了两次,马上我们就会看到。

OpenTGAFile() diff

一对寄存器(eax和ebx)在函数最后的部分故意交换了值。因为分配arg_4的值给寄存器的指令移动到函数后面。此外,例子中原来的位置,寄存器的值被设为0,下图比较中可以看出:

这是这个函数中有意义的部分。

OpenDIBFile() diff

这个函数中的变化类似OpenTGAFile()。新加入的局部变量赋值为0.然后,一些寄存器/变量使用的变化发生了,但是函数的意义没有变。

分析变化

推测OpenTGAFile()和OpenDIBFile()的变化由编译器优化导致是有道理的。因为它们和其他函数的意义没有明显的联系。

然而,LoadJPEGImageNewBuffer()的变化确实改变了代码的目的,清除了看上去像悬垂指针的指针。现在,可以推测这就是包含了CVE-2014-0301的安全补丁的函数。

怎样定位打补丁的函数

通过测试我观察qedit.dll是否是通过微软的软件如Excel,Outlook,PowerPoint,Word,Lync,Windows Media Player甚至是Adobe Acrobat装载的。但结果发现它们都不使用qedit。因此只可能是影响使用微软API的第三方软件的漏洞。

第三方软件,媒体播放器Media Player Classic(MPC)可以播放许多媒体类型,包括JPEG文件。当MPC载入JPEG图像时就会载入qedit.dll。下面的JPEG用来分析:

用Windbg附加MPC,然后断在LoadJPEGImageNewBuffer()函数上,可以跟踪到LoadJPEGImage()的调用。如果在eax中返回一个非零值,那么我们的漏洞代码路径就可以被执行了。

让LoadJPEGImage()执行失败

LoadJPEGImage()调用的几个函数可能会返回错误代码。因为无效的size值导致了很多解析不同文件格式中错误。我一开始想用二进制编辑器比如010 Editor打乱JPEG图像的长度值.通过使用JPG二进制模板,我简单地测试改变看上去像size的值,改成0x00,0xFF,0xFFFF。大多数我改变的值并没有引起函数的变化。最后,将Huffmann_Table结构体的htInfo改为0,成功触发了函数LoadJPEGImage()

htInfo的值设为0后,图片正常解析,但是调用LockBits()失败,错误7存储在eax中。解析一个有效的文件结果会把eax设为0,也就是ok的状态。根据MSDN documentationBitmap.LockBits(),错误代码被翻译成Win32Error,作为状态枚举的一部分。

错误被传递给ResetFormatBuffer(),直接跟在ConvertStatustoHR()后面。保存在eax中的状态是0x80004005. 现在,这个函数清空栈之后返回继续执行 LoadJPEGImageNewBuffer()。

崩溃

一旦执行权到LoadJPEGImageNewBuffer()手中,它会调用删除函数。下图是第一次释放

然后同样的对象调用了第二次释放:

第二次调用msvcrt!free(),程序在调用ntdll!RtlFreeHeap()时崩溃。

总结

希望你能享受这个比较打补丁和未打补丁制造crash的过程。漏洞利用作为读者的一个练习了。感谢漏洞分析过程中Bill Finlayson的帮助。

From Z1ng'Blog