[翻譯]Advanced Exploitation of Internet Explorer Heap Overflow(MS12-004)

仙果發表於2012-02-11
翻譯:Advanced Exploitation of Internet Explorer Heap Overflow Vulnerabilities (MS12-004)

題注:由仙果翻譯,其中肯定有錯誤之處,歡迎大家指正,感激不盡。
原始連結為:
http://www.vupen.com/blog/20120117.Advanced_Exploitation_of_Windows_MS12-004_CVE-2012-0003.php

嗨,大家好!
2012年剛剛開始CVE上就爆出了很多有趣的漏洞。其中一個就是CVE-2012-0003,一個危急級別的漏洞影響到了微軟多媒體庫並且與MIDI檔案處理流程有關。在上個星期已經作為MS12-004公告的一部分進行修補。
鑑於此漏洞的危急程度,我們建議儘快打上補丁。
從維基百科上,我們能知道:“MIDI 是一個1982年制定的行業標準協議”,基本上在它制定30年後,這種格式仍然觸發像微軟這樣的軟體廠商的漏洞。
單獨來說,這種漏洞非常常見但是漏洞利用並不容易,像Windows Media Player 或者Internet Explorer這樣的應用程式解析一個MIDI檔案時,
分配一個靜態的堆緩衝區但寫入卻是超過0x440個位元組(漏洞觸發原因)。為了處理如此大的分配大小,Internet Explorer下通用的利用技巧並不適用這個漏洞,
這才是真正有挑戰性的事情。
此部落格中,我們將演示說明此漏洞的危急程度,通過Internet Explorer 9/8/7/6繞過ASLR/DEP實現程式碼執行。

1.漏洞分析技巧
這是漏洞的關鍵點。一個MIDI檔案只要包含2種型別結構塊,一個名稱是MThd,另一個是MTrk。下列表格是2個部分的結構:
[翻譯]Advanced Exploitation of Internet Explorer Heap Overflow(MS12-004)
處理檔案之前,Windows Media 在”winmm.dll”中使用”mseOpen”函式分配2個緩衝區:
.text:76B5CDB1 mov edi, [ebp+arg_4]
.text:76B5CDB4 mov eax, [edi+10h]
.text:76B5CDB7 lea eax, ds:94h[eax*8]
.text:76B5CDBE cmp eax, 10000h
.text:76B5CDC3 mov [ebp+var_4], 7
.text:76B5CDCA jnb loc_76B5CED7
.text:76B5CDD0 push ebx
.text:76B5CDD1 push esi
.text:76B5CDD2 push eax
.text:76B5CDD3 call winmmAlloc(x)              //分配一個緩衝區
.text:76B5CDD8 mov esi, eax
.text:76B5CDDA xor ebx, ebx
.text:76B5CDDC cmp esi, ebx
.text:76B5CDDE jz loc_76B5CED5
.text:76B5CDE4 push 400h
.text:76B5CDE9 call winmmAlloc(x)             // 分配第二個大小為0x400的緩衝區

第二個緩衝區在接下來的處理中標記為”b1”。這個特殊的漏洞的觸發原因在於對”MTrk”資料塊特定事件的解析上。
這些事件首先通過quartz.dll 定義的函式 "smfReadEvents()"進行讀取。
 .text:74903483 loc_74903483:
.text:74903483 push [ebp+arg_C]
.text:74903486 lea eax, [ebp+var_14]
.text:74903489 push eax
.text:7490348A push esi
.text:7490348B call smfGetNextEvent(x,x,x) // 讀取一個事件並放在var_8(變數8)
74903490 test eax, eax
.text:74903492 jnz loc_749035B1
.text:74903498 mov ecx, [ebp+var_8]
.text:7490349B cmp cl, 0F0h
.text:7490349E jnb short loc_749034EC

它的首位元組作為事件的ID,並標示為e1 e2 e3,因此ECX=0x00e3e2e1.只有當事件 e1 < 0xF0時才是有趣的。
接下來的程式碼段為,在之前分配的陣列中偏移8的位置寫入事件。
 .text:749034B4 loc_749034B4:
.text:749034B4
.text:749034B4 mov eax, [esi+10h]
.text:749034B7 add eax, [ebp+var_14]
.text:749034BA movzx ecx, cl
.text:749034BD mov [edi], eax                    // 寫入第一個dword
.text:749034BF and dword ptr [esi+10h], 0
.text:749034C3 add edi, 4
.text:749034C6 and dword ptr [edi], 0         //寫入 0
.text:749034C9 movzx eax, byte ptr [ebp+var_8+2]
.text:749034CD movzx edx, byte ptr [ebp+var_8+1]
.text:749034D1 shl eax, 8
.text:749034D4 or eax, edx
.text:749034D6 shl eax, 8
.text:749034D9 add edi, 4
.text:749034DC or eax, ecx
.text:749034DE
.text:749034DE loc_749034DE:
.text:749034DE mov [edi], eax                    //寫入事件
.text:749034E0 add edi, 4
.text:749034E3 add dword ptr [ebx+8], 0Ch // 增加計數器
.text:749034E7 jmp loc_749035A2

這個陣列會在winmm.dll中的"midiOutPlayNextPolyEvent()" 函式中進行處理:
 .text:76B5D0B2 mov eax, [ebp+wParam]
.text:76B5D0B5 mov ecx, [ebx+eax]          // 讀取一個事件存放在ECX 中
.text:76B5D0B8 add ebx, 4
.text:76B5D0BB mov eax, ecx
.text:76B5D0BD mov [esi+24h], ebx
.text:76B5D0C0 shr eax, 18h 
.text:76B5D0C3 and ecx, 0FFFFFFh             // ecx = e3 e2 e1

接下來,程式對是否e1>7Fh進行判斷:
.text:76B5D1B6 loc_76B5D1B6:
.text:76B5D1B6 cmp [ebp+hmo], ebx
.text:76B5D1B9 mov esi, [edi+84h]
.text:76B5D1BF jz loc_76B5D276
.text:76B5D1C5 test cl, cl                         // cl = e1
.text:76B5D1C7 mov al, cl                        // al = e1
.text:76B5D1C9 mov ebx, ecx
.text:76B5D1CB js short loc_76B5D1E3     // jump if 80h <= e1 <= FFh
 [...]
.text:76B5D1E3 loc_76B5D1E3:
.text:76B5D1E3 mov edx, ecx
.text:76B5D1E5 shr edx, 8                       // dl = e2
.text:76B5D1E8 mov [edi+54h], cl
.text:76B5D1EB mov byte ptr [ebp+wParam+3], dl
.text:76B5D1EE shr ebx, 10h                   // bl = e3

解下來我們可以看到,e1&F0h=80h或者90h時Windows Media特有的處理事件流程:

 .text:76B5D1F1 loc_76B5D1F1:
.text:76B5D1F1 mov dl, al                      // dl = e1
.text:76B5D1F3 and dl, 0F0h
.text:76B5D1F6 cmp dl, 90h
.text:76B5D1F9 mov [ebp+var_1], dl
.text:76B5D1FC jz short loc_76B5D203
.text:76B5D1FE cmp dl, 80h
.text:76B5D201 jnz short loc_76B5D25F

這種情況下,在上面分配的緩衝區b1的某個偏移寫入資料,而這個偏移是通過e1和e2計算得來的:
.text:76B5D203 loc_76B5D203:
.text:76B5D203 movzx edx, byte ptr [ebp+wParam+3] // edx = e2
.text:76B5D207 and eax, 0Fh                                      // eax = e1 & 0Fh
.text:76B5D20A shl eax, 7 
.text:76B5D20D add eax, edx
.text:76B5D20F cdq
.text:76B5D210 sub eax, edx
.text:76B5D212 sar eax, 1
.text:76B5D214 cmp [ebp+var_1], 80h
.text:76B5D218 jz short loc_76B5D244


計算結果為:EAX = ((e1 & 0Fh) * 2^7 + e2) / 2。
這是b1緩衝區要被修改的資料。如果e1和e2為特定的值,那麼就有可能得到EAX>400h。例如,e1 = 9Fh => 0fh*2^7 = 780h,然後如果e2>7Fh,e1+e2>800h就使得EAX => 400h,程式寫入的資料就會越過分配的緩衝區的界限。
依據e1 & F0h = 90h 或80h 或者e3 = 1,有可能增加或減少一個任意位元組。例如e1 & F0h = 90h:
 .text:76B5D21E add esi, eax
.text:76B5D220 test byte ptr [ebp+wParam+3], 1
.text:76B5D224 mov al, [esi]                                   // 從b1中讀一個位元組
.text:76B5D23E inc al
.text:76B5D240 mov [esi], al  

如果e1 & F0h = 80h:
.text:76B5D248 lea edx, [eax+esi]
.text:76B5D24B mov al, [edx]                                 //從b1中讀一個位元組
 [...]
.text:76B5D25B dec al
.text:76B5D25D mov [edx], al    

因為b1的長度是0x400個位元組,當e1 = 8Fh 或 9Fh 且 e2=7Fh 時就觸發一個堆溢位。實際上,它使得破壞b1緩衝區之後的0x40位元組,以足夠實現任意程式碼執行成為可能。

2. 繞過ASLR/DEP的高階利用。

在Internet Explorer中可以載入漏洞模組,有可能做到穩定的缺陷利用。
我們知道winmm.dll 的"DllProcessAttach()"和mshtml.dll的"_DllMainStartup()"庫函式在他們的分配中使用了同一個堆:
In "DllProcessAttach()":
 .text:76B43F8F mov eax, large fs:18h                   // GetProcessHeap inlined
.text:76B43F95 mov eax, [eax+30h]
.text:76B43F98 mov eax, [eax+18h]
.text:76B43F9B mov _hHeap, eax

In "_DllMainStartup()":
.text:3CEAC930 call GetProcessHeap()
.text:3CEAC936 push eax
.text:3CEAC937 mov _g_hProcessHeap, eax

結果就是,它有可能利用漏洞破壞一個Internet Explorer的物件。
一般情況下,利用這型別漏洞的方法是找到大小等於漏洞緩衝區大小的一個物件。在此例中,可以使用以下方法來達到利用的目的:使用一個字串和一個物件連續分配漏洞緩衝區,一旦完成覆寫字串的長度就可以洩露出vTable(虛擬函式表) 並且推匯出mshtml.dll的基址:
[翻譯]Advanced Exploitation of Internet Explorer Heap Overflow(MS12-004)
然後分配一個新的緩衝區,觸發漏洞第二次覆寫物件的vTable(虛擬函式表):
[翻譯]Advanced Exploitation of Internet Explorer Heap Overflow(MS12-004)
另外,使用mshtml.dll的動態ROP程式碼進行堆噴射,從被修改的物件中呼叫一個函式就可以直接控制程式執行流程。
(不)幸運的是,這個方法需要漏洞觸發兩次而且只有它可能根據給定的大小去分配一個物件時才有效。給定的sizes < 0x100 位元組,IE為這種方法實現提供了足夠的物件,
但是MS12-004漏洞特定的情形下,緩衝區的長度是0x400位元組,在mshtml.dll中並沒有分配任意一個有趣的物件,並且在3FCh或者400h出引用了外部資料(意思可以理解為,因為緩衝區的長度為0x400,並且引用了外部資料,mshtml.dll並沒有分配可以利用的物件)。
建立一個特定的CImplAry物件則可以穩定的利用此漏洞。這個方法以建立的大小為0x400位元組的陣列為主要部分,精確地修改它的內容以洩露一個任意的vTable (虛擬函式表)並且重定向執行流程。
當克隆一個元素時,就會建立一個陣列作為例項。我們知道,"CElement::Clone()"函式呼叫"CElement::CloneAttributes()",並最終呼叫"CAttrArray::Clone()"來克隆屬性。以下程式碼屬於"CAttrArray::Clone()"函式:
.text:3D06A356 call CAttrArray::operator new(uint) // 分配一個新的 CAttrArray 物件
.text:3D06A35B cmp eax, edi
.text:3D06A35D jz short loc_3D06A368
.text:3D06A35F mov ecx, eax
.text:3D06A361 call CAttrArray::CAttrArray(void)   //初始化物件
.text:3D06A366 mov edi, eax
.text:3D06A368
.text:3D06A368 loc_3D06A368:
.text:3D06A368 test edi, edi
.text:3D06A36A mov esi, [ebp+arg_4]
.text:3D06A36D mov [esi], edi
.text:3D06A36F jz loc_3D0F5DE5

同時,程式校驗原先的元素是否包含一個屬性:
.text:3D06A375 mov eax, [ebx+4]
.text:3D06A378 shr eax, 2                                   // eax表示與原來元素相關聯的屬性數目
.text:3D06A37B js loc_3D053663
.text:3D06A381 cmp eax, [edi+8]
.text:3D06A384 jbe loc_3D0F5DF1
.text:3D06A38A push 10h
.text:3D06A38C call CImplAry::EnsureSizeWorker(uint,long)

如果這個數目不為空,IE將在"CImplAry::EnsureSizeWorker()"函式中分配10h*#attributes(元素屬性的數目)的陣列。因此,如果原始元素定義了0x40個屬性,IE就會精確地為新陣列分配0x400個位元組。這些屬性接下里使用"CAttrValue::Copy()"進行拷貝:
 .text:3D06A3D9 mov byte ptr [esi+1], 0
.text:3D06A3DD call CAttrValue::Copy

現在我們設想下列指令碼,包含建立一個新的“Select”元素並關聯不同的屬性和克隆程式碼:
var test = document.createElement("select")
test.obj0 = "AAAAAAAAAAAAAAAAAAAA"
test.obj1 = this
[...]
test.obj8 = alert
[...]
test.obj12 = new Date()

var cl0ne = test.cloneNode(true)

當克隆後,CImplAry 陣列如下所示:
[翻譯]Advanced Exploitation of Internet Explorer Heap Overflow(MS12-004)
實際上一個屬性定義包含3個部分,在記憶體中使用0x10個位元組。注意觀察圖3中的標註紅色的位元組。它們表示在MSDN中定義的不同型別:0x08表示一個字串,0x09表示一個物件,0x03為一個整型等等。
下圖為obj0, obj8 和obj12 在記憶體中的表示:
[翻譯]Advanced Exploitation of Internet Explorer Heap Overflow(MS12-004)
因為程式確切地依賴上述不同的型別來判斷屬性的型別(即通過不同的位元組如0x3,0x8或0x9來確定屬性的型別),它就有可能使用一個堆溢位來破壞這個陣列且強制瀏覽器混淆型別。圖5表示增加和減少兩個位元組之後的陣列:
[翻譯]Advanced Exploitation of Internet Explorer Heap Overflow(MS12-004)
圖5中,obj0(物件0)為一個物件而obj1為一個字串。通過使用JavaScript指令碼洩露物件的vTalbe(虛擬函式表)來繞過ASLR/DEP就成為可能。如下所示:
[翻譯]Advanced Exploitation of Internet Explorer Heap Overflow(MS12-004)
因此這個惡意的字串在函式"CAttrValue::GetIntoVariant()"使用並觸發漏洞併到一個CALL指令執行任意程式碼而不用管ASLR/DEP:
[翻譯]Advanced Exploitation of Internet Explorer Heap Overflow(MS12-004)
使用這種方法需要修改2個位元組。如果你只修改了一個位元組,請一定通知我!需要注意的是這種方法通用於所有Internet Explorer版本,包括9/8/7甚至IE6,使用Sizes > 0x230 進行堆分配(heapAlloc)。
就像部落格中演示的一樣,這個漏洞確實是危急程度,因此我們強烈建議儘快打上補丁。

POC及分析文件請參考:
【原創】Analysing the POC of CVE-2012-0003
【原創】新鮮出爐 - MS12-004
tranlate_Advanced Exploitation of Internet Explorer Heap Overflow Vulnerabilities (MS12-004).doc
tranlate_Advanced Exploitation of Internet Explorer Heap Overflow Vulnerabilities (MS12-004).pdf
VUPEN Vulnerability Research Blog - Advanced Exploitation of Internet Explorer Heap Overflow Vul.rar
上傳的附件:

相關文章