0%

cve-2010-2553Microsoft Cinepak Codec CVDecompress函数堆溢出漏洞

一、找到漏洞触发函数
windbg附加wmplayer.exe后通过!gflag +hpa开启页堆。在wmplayer加载POC文件后,程序在rep movs停下,并通过栈回溯找到

1
2
3
4
5
6
7
8
9
10
0:012> kb
ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
02c9fd30 73b7cbf3 00000004 00000000 00000068 iccvid+0x22cc
02c9fd60 73b766c8 001303b8 00000000 001200d8 iccvid!DllInstanceInit+0x6279
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\WINDOWS\system32\MSVFW32.dll -
02c9fdac 73b41938 001303b8 00000001 0000400d iccvid!DriverProc+0x1bf
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\WINDOWS\system32\quartz.dll -
02c9fdd0 7cf8fa9e 73b5b500 0000400d 02c9fde8 MSVFW32!ICSendMessage+0x2b
02c9fe00 7cf8f9e9 73b5b500 00000000 001200d8 quartz+0x1fa9e

ub iccvid!DllInstanceInit+0x6279
1
2
3
4
5
6
7
8
9
iccvid!DllInstanceInit+0x625e:
73b7cbd8 ffb698000000 push dword ptr [esi+98h]
73b7cbde 57 push edi
73b7cbdf ff7528 push dword ptr [ebp+28h]
73b7cbe2 ff752c push dword ptr [ebp+2Ch]
73b7cbe5 ff7530 push dword ptr [ebp+30h]
73b7cbe8 ff7514 push dword ptr [ebp+14h]
73b7cbeb ff765c push dword ptr [esi+5Ch]
73b7cbee e8bb55ffff call iccvid+0x21ae (73b721ae)

触发漏洞的函数(iccvid+0x21ae (73b721ae))。

二、分析漏洞触发原因
通过IDA分析该函数

1
2
3
4
5
6
7
8
9
; 84:               qmemcpy(
; 85: (void *)(*(_DWORD *)(v7 + 28) + v32),
; 86: (const void *)(*(_DWORD *)(v7 + 28) + v32 - 0x2000),
; 87: 0x2000u);
mov ecx, [ebx+1Ch]
lea edi, [ecx+eax]
mov ecx, 800h
lea esi, [edi-2000h]
rep movsd

反汇编,发现函数中调用了这个敏感函数,并且第三个参数为固定的0x2000。这里并无法判断问题所在,继续动态调试观察memcpy的源和目的数据,发现

1
2
3
4
5
6
7
8
9
10
11
12
0:008> p
eax=00000000 ebx=01f9ff20 ecx=00000000 edx=023bfd38 esi=01ce1b02 edi=00000000
eip=73b722ac esp=023bfd04 ebp=023bfd30 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
iccvid+0x22ac:
73b722ac 3bc7 cmp eax,edi
0:008> p
eax=00000000 ebx=01f9ff20 ecx=00000000 edx=023bfd38 esi=01ce1b02 edi=00000000
eip=73b722ae esp=023bfd04 ebp=023bfd30 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
iccvid+0x22ae:
73b722ae 7421 je iccvid+0x22d1 (73b722d1) [br=1]

由于该语句,跳转成功,导致跳过了漏洞函数的调用,但是漏洞触发是命中在跳转失败后的.text:73B722CC rep movsd指令中。可以推测该判断语句会不止命中1次,如图所示6-1,

因此在73B722CC下断点,发现在第四次命中后,继续程序的话会触发异常,因此跟进第四次的运行情况,观察数据复制前的寄存器状态和堆的状态为

1
2
3
4
5
eax=00006000 ebx=0271ec68 ecx=00000800 edx=0acffd38 esi=0016e008 edi=00170008
eip=73b722cc esp=0acffd04 ebp=0acffd30 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
iccvid!CVDecompress+0x11e:
73b722cc f3a5 rep movs dword ptr es:[edi],dword ptr [esi]

而且观察堆发现edi属于堆空间并且大小为0X6000h

1
2
3
4
5
6
7
8
9
10
0:016> !heap -a
Index Address Name Debugging options enabled
1: 000a0000
Segment at 000a0000 to 001a0000 (000e0000 bytes committed)
Segment at 026e0000 to 027e0000 (00003000 bytes committed)
Segment at 0aa60000 to 0ac60000 (000bf000 bytes committed)
2: 001a0000
Segment at 001a0000 to 001b0000 (00006000 bytes committed)
3: 001b0000
Segment at 001b0000 to 001c0000 (00003000 bytes committed)
1
2
3
4
5
6
7
8
9
10
11
12
0016e680: 00060 . 000e0 [01] - busy (d8)
0016e760: 000e0 . 00048 [01] - busy (3e)
0016e7a8: 00048 . 00030 [01] - busy (28)
0016e7d8: 00030 . 00060 [00]
0016e838: 00060 . 06008 [01] - busy (6000)
00174840: 06008 . 047c0 [10]
00179000: 00011000 - uncommitted bytes.
0018a000: 00000 . 00170 [01] - busy (164)
0018a170: 00170 . 00170 [01] - busy (164)
0018a2e0: 00170 . 00170 [01] - busy (164)
0018a450: 00170 . 00170 [01] - busy (164)
0018a5c0: 00170 . 00170 [01] - busy (164)

即0016e838-174838为申请的堆空间,而此次rep movs操作的复制的大小为2000h且目的地址为174840,因此此次操作导致了堆溢出覆盖了之后的堆结构

三、接下来分析漏洞触发的整体流程流程以及poc的构建。

首先需要程序调用73B721AE函数,且本地调用时的参数为(026dfea0,00f128f8,68,0005e800,0aa50050,540),通过IDA反汇编73B721AE函数,并动态跟进73B721AE主要关注其中的判断语句。
第一个判断为(a1+36)指向的数据值是否为0,如果不为0则调用localfree,应该是判断堆块是否被占用的。对构造poc来说无关紧要。
第二个判断 if ( a3=68h >= 0x20h )
第三个判断 直接观察反汇编指令为cmp [ebp+arg_8], eax。其中[ebp+arg_8]=a3=68h,eax=68h
第四个判断sub_73B72186的返回值是否为0,跟进sub_73B72186函数可以看到无论怎么跳转都会执行xor eax,eax将eax重置为0,因此不影响POC的构造。
**第五个判断if ( v13=\
(a2+10-1)=0x10h > 0 ) 第六个判断 if ( v29=0x5eh=[ebp+var_10] < 0x16 ) 第七个判断 if ( v29=0x5eh < v31=0x10h ) 第八个判断 if ( v14=10h == 10h || v14 == 11h )
第九个判断 if ( sub_73B72186(v31, 12, &a1) < 0 )会将eax变为0,不影响
第十个判断cmp eax,esi第一次两个值都为0,跳过了异常触发。而后三次会调用qmemcpy()函数,eax值分别为2000,4000,6000,会将堆中的数据向后移动2000h
第十一个判断*(a3+3)==0
第十二个判断*v14=a2+10==11h,若相等则进入qmemcpy()触发异常。
几个敏感参数,68h、5eh、10h、11h对比POC,判断这些值在POC中的含义。
我觉得接下来是最难的,因为找不到完整的相关资料,也没办法弄清楚几个判断的含义,基本上只能猜。
从网站
https://msdn.microsoft.com/en-us/library/ms779636(v=vs.85).aspx和https://multimedia.cx/mirror/cinepak.txt
并且用010editor来分析avi.poc文件
http://www.sweetscape.com/010editor/repository/templates/
发现strf(stream format)中bmiheader-bicompression为cvid,图6-2(没有找到相关资料说明,Cinepak数据是从哪里开始的),根据poc.py得知cinepak_codec_data1对应010editor分析出来的struct genericblock gb[0]中的char data部分。分析Cinepak结构,查看各字段的值可以看到第二个判断以及第三个判断的
0x68为CVID数据长度,第五个判断的10H为编码条数量,第六第七个判断的0x5E为未解压数据长度**,第八个判断的0x10为cvid chunkid,第十个判断的,第十二个判断的0x11为cvid chunkid。
而从第五个判断到第十二个判断为一个大循环,只要余下的判断满足,程序不break,程序便会一直运行,qmemcpy也会一直被调用,当调用超过3次后复制数据导致堆溢出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
eax=00002000 ebx=0271ec68 ecx=00000800 edx=0acffd38 esi=0016a008 edi=0016c008
eip=73b722cc esp=0acffd04 ebp=0acffd30 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
iccvid!CVDecompress+0x11e:
73b722cc f3a5 rep movs dword ptr es:[edi],dword ptr [esi]
eax=00004000 ebx=0271ec68 ecx=00000800 edx=0acffd38 esi=0016c008 edi=0016e008
eip=73b722cc esp=0acffd04 ebp=0acffd30 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
iccvid!CVDecompress+0x11e:
73b722cc f3a5 rep movs dword ptr es:[edi],dword ptr [esi]
eax=00006000 ebx=0271ec68 ecx=00000800 edx=0acffd38 esi=0016e008 edi=00170008
eip=73b722cc esp=0acffd04 ebp=0acffd30 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
iccvid!CVDecompress+0x11e:
73b722cc f3a5 rep movs dword ptr es:[edi],dword ptr [esi]

得出结论:将strf(stream format)中bmiheader-bicompression设置为cvid,使wmplay调用iccvid,cvid结构中设置CVID长度>=0x20,编码条数量>3则进入漏洞循环中。当chunk id为0X1100DE CVID CHUNK >= 3,并且后面的数据超过0x16则会调用qmemcpy函数,且每次操作目的地址递增0x2000,而其大小总共只有0x6000,导致循环3次后导致堆溢出。