<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/tips/3071

            0x00 寫在前面


            第一次在WooYun發文章,不知道是否符合眾客官口味,望輕拍。

            這篇文章翻譯至我的這篇博客,主要介紹了一種叫做BROP的攻擊,該文章主要介紹原理部分,對該攻擊的重現可以參看我的另外一篇博客

            BROP攻擊基于一篇發表在Oakland 2014的論文Hacking Blind,作者是來自Standford的Andrea Bittau,以下是相關paper和slide的鏈接:

            paper

            slide

            以及BROP的原網站地址:

            Blind Return Oriented Programming (BROP) Website

            可以說這篇論文是今年看過的最讓我感到興奮的論文(沒有之一),如果要用一個詞來形容它的話,那就只有“不能更帥”才能表達我對它的喜愛程度了!

            這篇文章假設讀者已經了解Return-Oriented Programming (ROP) 的基本概念,所以只是介紹BROP的實現原理,如果還不清楚什么是ROP,請先出門左轉,看看Wiki的相關介紹。

            BROP的實現真的是讓人感到非常“cool”和“smart”,我希望能夠通過這篇文章把它講清楚。

            0x01 BROP攻擊的目標和前提條件


            目標:通過ROP的方法遠程攻擊某個應用程序,劫持該應用程序的控制流。我們可以不需要知道該應用程序的源代碼或者任何二進制代碼,該應用程序可以被現有的一些保護機制如NX, ASLR, PIE, 以及stack canaries等保護,應用程序所在的服務器可以是32位系統或者64位系統。

            初看這個目標感覺實現起來特別困難。其實這個攻擊有兩個前提條件的:

            0x10 BROP的攻擊流程 1 - 遠程dump內存


            由于我們不知道被攻擊程序的內存布局,所以首先要做的事情就是通過某種方法從遠程服務器dump出該程序的內存到本地,為了做到這點我們需要調用一個系統調用write,傳入一個socket文件描述符,如下所示:

            write(int sock, void *buf, int len)

            將這條系統調用轉換成4條匯編指令,如圖所示:

            write gadgets

            所以從ROP攻擊的角度來看,我們只需要找到四個相應的gadget,然后在棧上構造好這4個gadget的內存地址,依次進行順序調用就可以了。

            但是問題是我們現在連內存分布都不知道,該如何在內存中找到這4個gadgets呢?特別是當系統部署了ASLR和stack canaries等保護機制,似乎這件事就更難了。

            所以我們先將這個問題放一放,在腦袋里記著這個目標,先來做一些準備工作。

            攻破Stack Canaries防護

            如果不知道什么是stack canaries可以先看這里,簡單來說就是在棧上的return address下面放一個隨機生成的數(成為canary),在函數返回時進行檢查,如果發現這個canary被修改了(可能是攻擊者通過buffer overflow等攻擊方法覆蓋了),那么就報錯。

            那么如何攻破這層防護呢?一種方法是brute-force暴力破解,但這個很低效,這里作者提出了一種叫做“stack reading”的方法:

            假設這是我們想要overflow的棧的布局:

            stack layout

            我們可以嘗試任意多次來判斷出overflow的長度(直到進程由于canary被破壞crash了,在這里即為4096+8=4104個字節),之后我們將這4096個字節填上任意值,然后一個一個字節順序地進行嘗試來還原出真實的canary,比如說,我們將第4097個字節填為x,如果x和原來的canary中的第一個字節是一樣的話,那么進程不會crash,否則我們嘗試下一個x的可能性,在這里,由于一個字節只有256種可能,所以我們只要最多嘗試256次就可以找到canary的某個正確的字節,直到我們得到8個完整的canary字節,該流程如下圖所示:

            stack reading

            我們同樣可以用這種方法來得到保存好的frame pointerreturn address

            尋找stop gadget

            到目前為止,我們已經得到了合適的canary來繞開stack canary的保護, 接下來的目標就是找到之前提到的4個gadgets。

            在尋找這些特定的gadgets之前,我們需要先來介紹一種特殊的gadget類型:stop gadget.

            一般情況下,如果我們把棧上的return address覆蓋成某些我們隨意選取的內存地址的話,程序有很大可能性會掛掉(比如,該return address指向了一段代碼區域,里面會有一些對空指針的訪問造成程序crash,從而使得攻擊者的連接(connection)被關閉)。但是,存在另外一種情況,即該return address指向了一塊代碼區域,當程序的執行流跳到那段區域之后,程序并不會crash,而是進入了無限循環,這時程序僅僅是hang在了那里,攻擊者能夠一直保持連接狀態。于是,我們把這種類型的gadget,成為stop gadget,這種gadget對于尋找其他gadgets取到了至關重要的作用。

            尋找可利用的(potentially useful)gadgets

            假設現在我們找到了某個可以造成程序block住的stop gadget,比如一個無限循環,或者某個blocking的系統調用(sleep),那么我們該如何找到其他 useful gadgets呢?(這里的“useful”是指有某些功能的gadget,而不是會造成crash的gadget)。

            到目前為止我們還是只能對棧進行操作,而且只能通過覆蓋return address來進行后續的操作。假設現在我們猜到某個useful gadget,比如pop rdi; ret, 但是由于在執行完這個gadget之后進程還會跳到棧上的下一個地址,如果該地址是一個非法地址,那么進程最后還是會crash,在這個過程中攻擊者其實并不知道這個useful gadget被執行過了(因為在攻擊者看來最后的效果都是進程crash了),因此攻擊者就會認為在這個過程中并沒有執行到任何的useful gadget,從而放棄它,這個步驟如下圖所示:

            useful gadget but crash

            但是,如果我們有了stop gadget,那么整個過程將會很不一樣. 如果我們在需要嘗試的return address之后填上了足夠多的stop gadgets,如下圖所示:

            stop gadgets usage

            那么任何會造成進程crash的gadget最后還是會造成進程crash,而那些useful gadget則會進入block狀態。盡管如此,還是有一種特殊情況,即那個我們需要嘗試的gadget也是一個stop gadget,那么如上所述,它也會被我們標識為useful gadget。不過這并沒有關系,因為之后我們還是需要檢查該useful gadget是否是我們想要的gadget.

            最后一步:遠程dump內存

            到目前為止,似乎準備工作都做好了,我們已經可以繞過canary防護,并且得到很多不會造成進程crash的“potential useful gadget”了,那么接下來就是該如何找到我們之前所提到的那四個gadgets呢?

            find write gadgets

            如上圖所示,為了找到前兩個gadgets:pop %rsi; retpop %rdi; ret,我們只需要找到一種所謂的BROP gadget就可以了,這種gadget很常見,它做的事情就是恢復那些callee saved registers. 而對它進行一個偏移就能夠生成pop %rdipop %rsi這兩個gadgets.

            不幸的是pop %rdx; ret這個gadget并不容易找到,它很少出現在代碼里, 所以作者提出一種方法,相比于尋找pop %rdx指令,他認為可以利用strcmp這個函數調用,該函數調用會把字符串的長度賦值給%rdx,從而達到相同的效果。另外strcmpwrite調用都可以在程序的Procedure Linking Table (PLT)里面找到.

            所以接下來的任務就是:

            尋找BROP Gadget

            事實上BROP gadgets特別特殊,因為它需要順序地從棧上pop 6個值然后執行ret。所以如果我們利用之前提到的stop gadget的方法就可以很容易找到這種特殊的gadget了,我們只需要在stop gadget之前填上6個會造成crash的地址:

            find brop gadget

            如果任何useful gadget滿足這個條件且不會crash的話,那么它基本上就是BROP gadgets了。

            尋找PLT項

            PLT是一個跳轉表,它的位置一般在可執行程序開始的地方,該機制主要被用來給應用程序調用外部函數(比如libc等),具體的細節可以看相關的Wiki。它有一個非常獨特的signature:每一個項都是16個字節對齊,其中第0個字節開始的地址指向改項對應函數的fast path,而第6個字節開始的地址指向了該項對應函數的slow path:

            plt structure

            另外,大部分的PLT項都不會因為傳進來的參數的原因crash,因為它們很多都是系統調用,都會對參數進行檢查,如果有錯誤會返回EFAULT而已,并不會造成進程crash。所以攻擊者可以通過下面這個方法找到PLT:如果攻擊者發現好多條連續的16個字節對齊的地址都不會造成進程crash,而且這些地址加6得到的地址也不會造成進程crash,那么很有可能這就是某個PLT對應的項了。

            那么當我們得到某個PLT項,我們該如何判斷它是否是strcmp或者write呢?

            對于strcmp來說, 作者提出的方法是對其傳入不同的參數組合,通過該方法調用返回的結果來進行判斷。由于BROP gadget的存在,我們可以很方便地控制前兩個參數,strcmp會發生如下的可能性:

            arg1 | arg2 | result
            :--: | :--: | :--:
            readable | 0x0 | crash
            0x0 | readable | crash
            0x0 | 0x0 | crash
            readable | readable | nocrash
            

            根據這個signature, 我們能夠在很大可能性上找到strcmp對應的PLT項。

            而對于write調用,雖然它沒有這種類似的signature,但是我們可以通過檢查所有的PLT項,然后觸發其向某個socket寫數據來檢查write是否被調用了,如果write被調用了,那么我們就可以在本地看到傳過來的內容了。

            最后一步就是如何確定傳給write的socket文件描述符是多少了。這里有兩種辦法:1. 同時調用好幾次write,把它們串起來,然后傳入不同的文件描述符數;2. 同時打開多個連接,然后使用一個相對較大的文件描述符數字,增加匹配的可能性。

            到這一步為止,攻擊者就能夠將整個.text段從內存中通過socket寫到本地來了,然后就可以對其進行反編譯,找到其他更多的gadgets,同時,攻擊者還可以dump那些symbol table之類的信息,找到PLT中其它對應的函數項如dup2execve等。

            0x11 BROP的攻擊流程 2 - 實施攻擊


            到目前為止,最具挑戰性的部分已經被解決了,我們已經可以得到被攻擊進程的整個內存空間了,接下來就是按部就班了(從論文中翻譯):

            歸納起來,BROP攻擊的整個步驟是這樣的:

            0x100 后記


            以上就是BROP攻擊的原理,在這篇博文中重現了這個攻擊,有興趣的可以去看看。

            其實在整個攻擊過程中最酷的要數第一個步驟:如何dump內存,之后的步驟其實就是傳統的ROP攻擊了。明白了原理之后,其實最好的了解該攻擊的方法就是看源代碼了,這個對了解整個ROP會有非常大的幫助。

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

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

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

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

                      亚洲欧美在线