漏洞概要
该漏洞是一个栈溢出漏洞,该漏洞是由于Microsoft Windows Common Controls的MSCOMCTL.TreeView、
MSCOMCTL.ListView2、MSCOMCTL.TreeView2、MSCOMCTL.ListView控件(MSCOMCTL.OCX)中存在错误,可被
利用破坏内存,导致任意代码执行。
漏洞分析
目前在互联网上已经出现了利用该漏洞的DOC文件,下面以其中的一个文件为基础分析该漏洞的产生原因。下面是该
文件的截图
在该文件中我们首先找到\object\objocx这个标签,这个标签标志着完整的objdata数据的起始。在objdata的后面以“D0CF11E0”这样的数据开头的部分便是objdatatext。在这里我们找到了ListView的ClsID:BDD1F04B-858B-11D1-B16A-00C0F0283628。这个Clsid便是出现问题的几个ID之一。
之后在继续查找EleName为Contents的OLESSDirectoryEntry结构
正是在解析该结构中的Data数据时出现了问题。如下图:
Data数据截图
我们看究竟是如何解析的该段数据的。
读入Contents数据块,并创建相关的数据流对象。
读入Stream的头部,校验头部是否为固定的数据0x12344321,即之前我们看到的Data数据的头部。
注意:这里在操纵读入的数据流时,使用的是栈读取数据,之后正是由于没有对读取的数据进行大小的合法性校验,造成了栈溢出。
再简单介绍下CExposedStream::Read这个函数:
该函数的第二个参数为要读入数据返回的buffer,第三个参数为要读入数据的大小。很明显这里,将读入的数据返回到
了8字节的栈空间中。
比较Stream头部标志之后的数据是否为8,如果不是则直接返回错误。
再继续读取8个字节,读取完的数据如图中蓝色阴影部分。之后的0x2758F97F处的函数对于刚才读取的这8个字节做了
处理和校验。具体目的不详。
成功则返回0。
再读取0x1C个字节的数据
继续比较一个标志是否为0xEB7D084E,如果是则进入其对应的处理例程。如果不是则比较是否是0x58DA8D8B或者
0xF4D83600并进入相应的处理例程。
拷贝该结构的后面0xC个字节
根据得到的size再读取对应size大小的数据。此处大小为0x1C。
下面是对该段数据进行处理,与本文漏洞无关,略去分析。
读取下面一个结构为0xC的结构
同样是对该结构数据进行一些校验和处理。
注意这里传进来一个8,是checksize,主要作用是做接下来读取的数据的检查。另外在这里传进来的第一个参数
也是栈上的,该参数用于接收0x275C876D返回的数据。
继续读取4个字节(图“Data数据”中偏移0x62处的数据)
读出来为8
我们可以看到之后用刚才读出来的8作为申请内存的size。在申请内存之前将其与刚才传进来的参数8做了检查,因此可
以判断此处需要申请的buf必须为8。否则则直接返回错误。
之后在申请的8字节的缓冲区中读入数据。
将刚才读出来的数据拷贝到上个函数传进来的第一个参数的地址中。(一个栈中的地址)
之后该函数返回后,对刚才返回的数据进行校验。
解析下一个结构的函数入口
由checksize得知,函数0x275C876D需要读入的下一个Dword为0xC,也即为其要申请的buf大小。
读取的数据为0xC,符合条件。继续申请0xC字节的大小的buf。
读取到的0xC字节的数据如图蓝色阴影所示。
同样将刚才读出来的数据拷贝到上个函数传进来的第一个参数的地址中。(一个栈中的地址)
注意图中的注释。猜测该处传入的checksize应该为小于等于8,而这里只验证了小于8的情况,而没有验证大于8的情况。此处为0x8282
再读入4字节,仍然是0x8282。
随后便在堆上申请了0x8282字节的内存。
读取了0x8282字节的数据到刚才申请的堆中。(布置好的shellcode)
悲催的拷贝到了上个函数分配的8字节的栈内存中。
注意图中的注释
构造符合跳转到较近retn指令条件的数据。(两次检查)
此时栈中的数据如图。
由于系统版本的原因,0x7FFA24CE并非一个有效指令的地址。通过分析发现,作者可能需要找一个jmp esp指令。之后
便可以从栈中执行已经布置好的shellcode。该shellcode的缺点是直接从栈中执行了指令,无法绕过DEP等保护技术。
相信较好的shellcode会马上出来。
Shellcode截图
总结:MSCOMCTL.OCX在解析数据时候将需要处理的数据拷贝到了栈上,其中在解析一个标志为Cobj的结构的
时候对其后面需要拷贝的Data数据大小未做正确校验,导致可以将数据直接覆盖到栈中,造成了一个标准的栈溢
出漏洞。