<span id="7ztzv"></span>
<sub id="7ztzv"></sub>

<span id="7ztzv"></span><form id="7ztzv"></form>

<span id="7ztzv"></span>

        <address id="7ztzv"></address>

            原文地址:http://drops.wooyun.org/papers/5460

            0x00 概述


            作者:Francisco Falcón

            地址:https://blog.coresecurity.com/2015/03/25/exploiting-cve-2015-0311-part-ii-bypassing-control-flow-guard-on-windows-8-1-update-3/

            三月初我們發布了一篇分析CVE-2015-0311(Flash Player的UAF)的博文,我們概述了如何在Windows 7 SP1上進行利用。我們在博文的末尾提到,該博文敘述的利用過程并不適用于較新版本的Windows,如Windows 8.1 Update 3,原因是新操作系統平臺所采用的的漏洞利用緩解技術---|控制流防護(CFG)

            CFG---|微軟于2014年11月在Windows 8.1 Update 3上推出的一種技術---|在每一個間接調用前都增加了一個檢查,用以檢查所調用的目的地址是否是編譯時所確定的“安全”位置之一。如果運行時檢查失敗,程序就認為存在試圖改變正常執行流程的操作,并立即退出。

            在CFG出現之前,利用諸如UAF之類的漏洞進行任意內存讀寫等基本操作--如我們在CVE-2015-0311中所做的--通常意味著需要繞過ASLR和DEP然后最終穩定執行代碼。CFG的特別之處就在于,即使能夠進行內存的讀寫等操作,最終想要執行代碼還是需要付出可觀的額外努力。

            事實證明集成到Windows 8.1 Update 3中的IE11的Flash版本在編譯時啟用了CFG,因此通用的利用方法--將對象虛函數表(vtable)覆蓋為指向攻擊者控制的數據的指針,然后調用對象虛函數的方法不再可用。

            下面來看一下我們是如何繞過CFG對Win8.1 Update 3上IE11的Adobe Flash Player進行漏洞利用的。這篇文章假定你已經讀過part I(Exploiting CVE-2015-0311: A Use-After-Free in Adobe Flash Player),因此跳過CVE-2015-0311本身的細節的討論,直接進入到劫持瀏覽器進程的執行流上來。

            0x01 CFG技術簡介


            下圖所示,就是在沒有啟用CFG的Flash版本中,調用Vector對象的toString()方法時間接引用被覆蓋的虛函數表(vtable)的代碼,上一篇博文中也提到過:

            而下面是Win8.1 Update3的Flash 16.0.0.287(編譯時啟用CFG)執行同樣操作的代碼:

            ___|guard_check_icall_fptr函數指針指向**ntdll!LdrpValidateUserCallTarget**,該函數負責檢查間接調用的目的地址是否合法。當檢查到假的虛函數表,改變正常執行流的企圖就會被發現,然后程序轉向**ntdll!RtlpHandleInvalidUserCallTarget**函數,該函數通過發出一個INT 29h的中斷立刻結束進程。

            如想了解更多關于CFG內部機制,我推薦看一下MJ去年在Power Of Community安全會議上的演講“Windows 10 Control Flow Guard Internals”

            0x02 解決方法


            CFG技術是通過在編譯時添加大量對防護函數的調用以保護每一個間接調用的。Win8.1 Update 3的Flash Player 16.0.0.287包含了29238次對防護函數的調用。:

            那么,就存在如下幾種可以在CFG啟用的情況下修改執行流的方法:

            第一種方法要求能夠獲取到一個棧地址。盡管在改變了Vector長度后獲得了任意讀寫能力,在只知道指向堆中對象的指針的情況下,對于棧地址我還是沒有辦法獲取到。

            第二種方法會在利用中引入受感染軟件之外的新的依賴,因此這種方法可能也不是很理想。

            我找到了一些未被CFG保護的間接調用,如下例所示:

            該函數指針位于.data段中,而數據段是具有讀寫權限的,因此覆蓋該地址絕對是有可能實現的。然而,這種方法的主要問題在于你必須能夠影響程序的執行流程(在還沒有得到代碼的執行能力之前),以使修改后的函數指針最終是可被調用的。那么如果該函數僅在一些非常見的情況下調用(罕見的極端情況下調用)怎么辦?又或者如果該函數僅在進程初始化的早期調用怎么辦?

            使用這種方法的另一個障礙在于如果被覆蓋的函數指針被調用時沒有CPU寄存器指向我們自己的數據附近,想要轉換棧以開始運行ROP鏈將會變得非常棘手。

            目前為止,CFG在保護超過29000個Adobe Flash Player的間接調用上做的已經很好了。那么現在的問題就變為:我們能否在Flash Player中找到未被CFG保護的間接調用,并且能夠以簡單的方式影響程序的執行流程,使其調用被覆蓋的函數指針?

            如前面所述,CFG只保護那些可在編譯時確定的間接調用。下一個問題自然就變成:Flash Player中有不在編譯時生成的間接調用嗎?答案是肯定的:當然有!

            Flash Player包含有一個JIT(Just-In-Time)編譯器,可以即時將ActionScript虛擬機字節碼翻譯成本機的機器碼以提高執行速度。JIT生成的代碼中存在間接調用,又因為代碼是在運行時生成的,因此這些代碼里的間接調用并未被CFG保護。

            0x03 尋找未被保護的間接調用


            讓我們回到上一篇文章漏洞利用過程中的某處,在該處我們可以修改Vector對象的元數據,我們可以通過調用si32(0xffffffff,0x24)來設置Vector的長度為0xffffffff,而這個新的長度可以讓我們在進程的地址空間中讀寫任意內存數據。同時記得我們構造了一個ByteArray對象,我們的ROP鏈就保存為Vector的第一個元素。而存儲在ByteArray+8位置的dword就是指向虛函數表對象的指針(類的真實名稱就是VTable_object,在core/VTable.h中定義):

            現在,讓我們仔細檢查一下VTable_object,我們可以看到它包含了很多指針:

            我們試著跟進幾個指針(在下面的圖片中我跟進了VTable_object + 0xD4位置的指針),就會發現它們都看起來都差不多一樣。這些都是MethodEnv對象(在core/Method.h中定義的):

            MethodEnv對象中的第一個dword是指向其虛函數表的指針,第二個dword是一個函數指針(本例中為0x601C0A70)

            MethodEnv對象VTable_object + 0xD4位置的指針被間接引用,從而產生一個間接調用,調用MethodEnv_object+4(0x601c0a70)處的函數指針,我們注意到,此處的間接調用代碼是Flash JIT編譯器生成的,因此是未被CFG保護的!而且這個未保護的間接調用是可以通過調用ByteArray對象的toString()方法穩定觸發的。

            我們可以在VTable_object + 0xD4位置設一個硬件斷點,當我們的ActionScript代碼調用this.the_vector[0].toString()時,由JIT生成的代碼讀取VTable_object + 0xD4位置的指針,該斷點就會命中。注意此處函數指針是從ECX+4中取出(MOV EAX, DWORD PTR DS:[ECX+4]),并直接地進行調用,并未先調用CFG防護函數進行安全驗證:

            位于堆中的由JIT生成的代碼,是通過如下路徑執行到的:首先BaseExecMgr::invokeGeneric方法(core/exec.pp中定義)調用BaseExecMgr::endCoerce:

            #!c++
            // Invoker for native or jit code used before we have jit-compiled,
            // or after JIT compilation of the invoker has failed.
            Atom BaseExecMgr::invokeGeneric(MethodEnv *env, int32_t argc, Atom* atomv)
            {
                MethodSignaturep ms = env->get_ms();
                const size_t extra_sz = startCoerce(env, argc, ms);
                MMgc::GC::AllocaAutoPtr _ap;
                uint32_t *ap = (uint32_t *)avmStackAlloc(env->core(), _ap, extra_sz);
                unboxCoerceArgs(env, argc, atomv, ap, ms);
                return endCoerce(env, argc, ap, ms);
            }
            

            然后BaseExecMgr::endCoerce調用JIT生成的函數(該函數我們在上上段中講述過,其中包含未受CFG保護的間接調用):

            #!c++
            Atom BaseExecMgr::endCoerce(MethodEnv* env, int32_t argc, uint32_t *ap, MethodSignaturep ms)
            {
            [...]
            switch(bt){
            [...]
            default:
            {
                STACKADJUST(); // align stack for 32-bit Windows and MSVC compiler
                const Atom i = (*env->method->_implGPR)(env, argc, ap);
            [...]
            

            如下是BaseExecMgr::endCoerce的二進制代碼:

            0x04 利用


            我們已經了解了如何觸發一個未受CFG保護的間接調用,現在我們需要做的是在VTable_object + 0xD4位置放一個虛假的MethodEnv對象。有圖有真相,我們可以在下圖中直觀地看到對象之間的初始狀態以及調用關系(點擊圖片可放大):

            在ActionScript利用代碼中,我們通過Nicolas Joly的基于Number對象的讀取方法可以獲取到ByteArray object + 8位置的指針,如上一篇博文中提到的:

            #!c++
            var vtable_object:uint = leak_8_bytes(bytearray_object_pointer + 8);
            

            然后指針值 + 0xD4得到我們想覆蓋的目標地址,之后再計算出Vector對象(其長度我們已經覆蓋為0xffffffff)中我們需要使用的定位,以寫入目標地址:

            #!c++
            var target_address:uint = vtable_object + 0xd4;
            /* 0x28: offset of the first element within the Vector object */
            var idx: uint = (target_address - (address_of_vector + 0x28)) / 4;
            this.the_vector[idx] = address_of_rop_chain >> 3;
            

            我們將VTable_object + 0xD4位置存放的指針覆蓋為ROP鏈(存儲在上面的ActionScript代碼片中的 address_of_rop_chain變量中)的地址。同時注意我將address_of_rop_chain右移了三次;這是因為 address_of_rop_chain是unit類型的,而ActionScript虛擬機內部存儲機制中,unit類型的值是要先左移3次然后再異或6(6代表“Integer”標簽---|我們上一篇博文中提到過)。

            最后,我們只需調用下ByteArray對象(存儲于this.the_vector[0])的toString()方法,就可以觸發JIT生成代碼中的未經CFG保護的間接調用,然后就會啟動我們的ROP鏈。

            #!c++
            new Number(this.the_vector[0].toString());
            

            注意,未保護的間接調用CALL EAX 調用我們控制的函數指針時,ECX寄存器就指向我們的ROP鏈,因此pivoting stack(一種棧轉換技術--指向的堆空間變成新的棧)開始執行ROP鏈就變得很簡單。

            0x05結論


            控制流防護(CFG)是一種有用的緩解技術,它可以增加利用代碼編寫的難度和成本。但是就像任何一種已知的漏洞利用緩解技術一樣,在特定的環境下,其不可避免的存在繞過的方法。

            本文介紹的繞過方法,利用了JIT生成代碼中未經保護的間接調用,該方法不僅適用于Adobe Flash Player,任何使用了JIT編譯器的軟件都潛在地可以如此利用,因為運行時生成的代碼是不會受到CFG保護的,除非開發者能夠付出額外的努力將JIT編譯器生成的代碼固化。

            請注意Flash Player的JIT編譯器其實之前就已經被利用過不止一次,用以繞過各種緩解機制:

            Dion Blazakis 2010年的“Pointer inference and JIT spraying”

            Fermin Serna 2013年的“Flash JIT – Spraying info leak gadgets”

            <span id="7ztzv"></span>
            <sub id="7ztzv"></sub>

            <span id="7ztzv"></span><form id="7ztzv"></form>

            <span id="7ztzv"></span>

                  <address id="7ztzv"></address>

                      亚洲欧美在线