一、找到漏洞触发函数
windbg附加wmplayer.exe后通过!gflag +hpa开启页堆。在wmplayer加载POC文件后,程序在rep movs停下,并通过栈回溯找到1
2
3
4
5
6
7
8
9
100: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
1 | iccvid!DllInstanceInit+0x625e: |
触发漏洞的函数(iccvid+0x21ae (73b721ae))。
二、分析漏洞触发原因
通过IDA分析该函数
1 | ; 84: qmemcpy( |
反汇编,发现函数中调用了这个敏感函数,并且第三个参数为固定的0x2000。这里并无法判断问题所在,继续动态调试观察memcpy的源和目的数据,发现
1 | 0:008> p |
由于该语句,跳转成功,导致跳过了漏洞函数的调用,但是漏洞触发是命中在跳转失败后的.text:73B722CC rep movsd指令中。可以推测该判断语句会不止命中1次,如图所示6-1,
因此在73B722CC下断点,发现在第四次命中后,继续程序的话会触发异常,因此跟进第四次的运行情况,观察数据复制前的寄存器状态和堆的状态为
1 | eax=00006000 ebx=0271ec68 ecx=00000800 edx=0acffd38 esi=0016e008 edi=00170008 |
而且观察堆发现edi属于堆空间并且大小为0X6000h
1 | 0:016> !heap -a |
1 | 0016e680: 00060 . 000e0 [01] - busy (d8) |
即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
15eax=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次后导致堆溢出。