CVE-2012-0158浅析-office栈溢出漏洞,


本文转载自
永远的经典:CVE-2012-0158漏洞分析、利用、检测和总结
Recycling Known Vulnerabilities - Old Cyber Attack Goes Stealth

漏洞环境

Win7
Office2007(MSCOMCTL.OCX:6.1.95.45)
SHA256:09700a4d4979dfb98ce3a04db59efd8d522d5f06bee756af767dc94519a604fd

分析过程

漏洞原理

该样本采用的免杀技术较为复杂,稍后详细讲解。这里先用metasploit生成一个poc来说清楚漏洞的原理。下图为生成的样本rtf文件在Notepad++中打开的视图(稍作整理),\objocx关键字代表嵌入了一个ActiveX控件对象,\objdata后面的数据代表了控件数据,控件以ole格式存储在rtf文档中,此部分数据将会被读入winword.exe进程内存并被解析。

用rtfobj.py工具来提取里面嵌入的ole对象。

将提取的OLE对象用OleFileView打开,可以看到包含三个Stream:Contents,ObjInfo和OCXNAME。Contents流内存储着比较多的数据,猜测该Stream应该代表着控件数据。

ObjInfo流内只存储了几个字节。

OCXNAME流内存储了控件名称。

再将提取出的对象拖入offvis,定位到CLSIDbdd1f04b-858b-11d1-b16a-00c0f0283628。

可以看到ListView控件对应的模块是MSCMOCTL.OCX,一般看到这里你就知道分析时需要在对哪个模块下加载断点了,当然,在面对一个全新样本时,直到这里我们还无法确定漏洞是如何造成的,但可以推测漏洞与MSCMOCTL.OCX模块有关。

由前面的分析已知这个样本会弹出计算器,在CreateProcessA和CreateProcessW设置两个断点,断下后看到栈被破坏得很严重,并不能准确定位到是哪个模块出的问题,向前向后回溯栈和看寄存器也无法得到进一步的有效信息。这时我们可以装个EMET,打开样本后,借助EMET的记录,通过在事件管理器里面定位DEP、EAF、ROP等相关记录的地址来进一步定位漏洞的触发点(如jmp esp)。这里采取另一种方式,既然这个样本要加载一个OLE对象,对OpenStream函数的调用肯定是跑不掉的,于是在windbg里面搜寻OpenStream相关函数。

ole32!CexposedDocFile::OpenStream函数看上去比较有趣,我们来看一下微软对于OpenStream函数的定义。有趣的是它的第一个参数,很明显,这个参数是一个unicode字符串,代表了stream的名称。


当然,考虑到this指针的传递,所以这个函数在ole32内的实际实现如下图所示,代表stream名称的是第2个参数。

现在,我们在调试器里面设置如下断点。

可以看到在打开Contents流后,计算器就弹出来了。而前面静态分析中看到的其他两个流并没有输出,初步推测是MSCOMCTL.OCX模块在解析内嵌控件的Contents流时出了问题。对MSCOMCTL.OCX模块下模块加载断点sxe ld:mscomctl,以避免打开文档中其他无关流对象时在OpenStream断下。重启windbg,接下来在ole32!CexposedDocFile::OpenStream函数打开Contents流时停下,通过Code Coverage工具得到当前漏洞触发的整个执行流。蓝色代表栈回溯;橙色代表每次调用CExposedStream::Read的位置;紫色为最终的栈溢出点。

实际调试时发现winword每次从流中读取文件都会调用CExposedStream::Read函数。

其中第二个参数是带读入数据的缓冲区指针,第三个参数为需要读入的数据大小,最后一个参数为一个指向int型数据的指针,返回实际读的字节数。重启windbg,在加载MSCOMCTL.OCX模块后,对CExposedStream::Read函数下断点并进行相应输出,可以看到每次Read的数值如下图所示,和上图中的注释相对应。

这里引用常熟理工学院乐德广老师的一篇论文《面向RTF的OLE对象漏洞分析研究》中的一幅图来表示此处的数据校验流程(原图中有一些小错误,如最后读入的应该是0x0c而不是0x14字节,我这里按照本次样本的实际情况重新画了一下解析流程,另有部分额外的校验未出现在图中)。

我们来对照Contents流中的数据看一下具体的读入情况,如图所示,相同的颜色为每次读入的数据,带黄色高亮的为需要校验的数据,斜体代表数据在代码中有校验,粗体代表读入的是一个长度,随后的数据会根据这个长度进行读入。

分析到这里,自然很想看一下一个正常插入的ListView对象所对应的数据应该是什么样的。我们来对比一下漏洞样本和正常样本,紫色为数据校验过程中两者相同的地方,橙色为漏洞样本出问题的长度域。可以看到正常样本中的两个长度域恒为8,但漏洞样本中这两个值被精心构造成了0x8282。

分析到这里已经很清楚了:MSCOMCTL.OCX组件在解析ListView控件的Contents内容时,在某次读入0x8282长度时导致了栈溢出,根据日志中的栈回溯,我们能迅速定位到漏洞发生处,一番排查之后,发现发生溢出的根本原因是因为将一个小于等于号写成了大于等于号。

这个漏洞通过Fuzz很难发现,因为构造数据时既不知道要保证前后两处的长度相等,又容易因字节数据错位而通不过校验。

免杀技术

其实漏洞分析到此也就结束了,只不过偶然看到了去年的一篇文章讲一个比较有趣的bypass杀软的样本,当时VT只有一家检测出来,我今天在VT上又看了看,只有四家能检测。

这就比较NB了,毕竟这是5年前的洞了,当然这肯定也不是一般的脚本小子能写出来的,可能也是用于APT攻击。下面我们来详细分析一下。
1.成功利用漏洞之后JMP ESP跳向shellcode1;

2.shellcode1有一些垃圾指令,依次解密出shellcode2和shellcode3;

3.shellcode3通过模块名称长度定位kernel32,通过遍历PEB和kernel32来定位函数,遍历现有的句柄读取magic number定位.xls文件,调用VirtualAlloc分配可读可写可执行的内存,将shellcode4拷贝过去并解密执行;



4.shellcode4整体流程:

(1)从PEB定位更多的函数,但此时它保存时会加0x5的偏移;

(2)加载msvbvm60.dll,从这个模块中定位PutMemVar函数,并用一个能够接收任何函数指针+0x5作为参数的函数替换它,每次调用API都会从PutMemVar发起(绕过根据调用栈判断exe/dll的杀软),并且每次调用都判断有没有inline hook(绕过杀软的应用层hook和调试器int3断点);


(3)使用PutMemVar解密.xls文件中最终的恶意程序,然后修复原来的文件使其能正常显示,之后再创建一个挂起的Excel子进程,用解密出的恶意程序覆盖它的内存,修改ImageBase指向其内容的地址,最后恢复新进程运行(绕过白名单和扫描可执行文件的杀软)。


相关内容

    暂无相关文章