摘要近幾十年來,計算機軟件經(jīng)歷著一場利用技術(shù),、發(fā)現(xiàn)與保護技術(shù)之間的軍備競賽,。一些有效的保護措施(例如,,ASLR地址空間布局隨機化),顯著地增加了成功利用一個漏洞的難度,。一個現(xiàn)代的漏洞利用一般分為兩個階段:第一步需要進行信息泄漏以取得程序的內(nèi)存分布,,接著第二步則進行實際的利用。然而,,由于內(nèi)存破壞后的具體情況各不相同,,從程序中得到內(nèi)存布局的方法并不總是可行。 在這篇文章中,,我們展示了一種不需要進行信息泄漏,,而是使用動態(tài)裝載器來直接標識關(guān)鍵(critical)函數(shù)的位置并調(diào)用它們的技術(shù)。我們在ELF文件標準和動態(tài)裝載器的實現(xiàn)中找到了幾個弱點,,這些弱點能夠用來解析,、執(zhí)行任意庫函數(shù)。因此,,我們能夠繞過特定的安全緩解措施,,包括專門為保護ELF數(shù)據(jù)結(jié)構(gòu)不被攻擊者破壞而設計的partial RELRO與full RELRO。我們實現(xiàn)了一個名為Leakless的原型工具,,并針對動態(tài)裝載器的實現(xiàn),、之前的攻擊技術(shù)和真實案例進行評估以確定我們的發(fā)現(xiàn)的影響。另外,,Leakless也可以進行更可靠,、更不具侵略性的攻擊,以減少被入侵檢測系統(tǒng)發(fā)現(xiàn)的幾率,。 簡介從1998年Morris worm發(fā)表的第一個被廣泛應用的棧溢出文章[27]以來,,內(nèi)存破壞漏洞的保護、利用和減緩技術(shù)研究占據(jù)著安全研究人員和網(wǎng)絡罪犯相同的時間,。盡管近年來內(nèi)存破壞漏洞的盛行趨勢有所減緩,,經(jīng)典的棧溢出仍然雄踞最常見軟件漏洞的第3位,而另外4種內(nèi)存破壞漏洞已經(jīng)跌出了前25名,。 而能夠剎住內(nèi)存破壞之風的原因,,是在內(nèi)存保護與緩解措施方面的巨大投入。這些緩解措施主要應用于2個方面:系統(tǒng)級加固(例如CGroups [23], Apparmor [4], Capsicum [41], 和 GRSecurity [18]) 和應用級加固(如 stack canaries [3], Address Space Layout Randomization (ASLR), 和 No-eXecute (NX) bit [8]). 尤其是地址空間布局隨機化(ASLR),,通過將動態(tài)庫加載到內(nèi)存中隨機的一塊區(qū)域(對于攻擊者來說未知),,使得攻擊者需要將利用過程拆分為2個階段。在第一個階段,,攻擊者必須使用一個信息泄漏漏洞將程序以及動態(tài)庫的內(nèi)存布局泄漏出來,,這樣就可以標識出安全關(guān)鍵(security-critical)函數(shù)(例如system()庫函數(shù))代碼的地址。在第二個階段,攻擊者使用一個控制流重定向漏洞,,將程序的控制流重定向到這個函數(shù),。 然而,由于內(nèi)存破壞后的具體情況各不相同,,從程序中得到內(nèi)存布局信息的方法并不總是可行,。例如,,大多數(shù)解析代碼(例如解碼圖像或者視頻)經(jīng)常不會與攻擊者有直接的交互,,這就排除了信息泄漏的可能性。沒有這些信息,,再對ASLR保護下的二進制文件使用現(xiàn)在的技術(shù)進行利用通常是不可行或不可靠的,。 就像 [36] 中寫的那樣,除了加固應用和系統(tǒng)的競賽,,對于二進制的格式以及系統(tǒng)組件的一些鮮為人知的角落,,則缺少仔細的檢查。特別地,,我們著眼于操作系統(tǒng)中的一個用戶態(tài)組件——動態(tài)裝載器,,負責裝載二進制文件以及它們依賴的庫文件到內(nèi)存中。二進制文件使用動態(tài)裝載器來支持導入符號的解析功能,。有趣的是,,這恰好就是一個面對加固應用的攻擊者通過泄漏庫地址與內(nèi)容嘗試“重塑”一個符號的表現(xiàn)。 我們的技術(shù)的亮點,,在于可以通過活用一個動態(tài)裝載器的功能,,完全省去對信息泄漏漏洞的需要。我們的技術(shù)利用動態(tài)裝載器與ELF格式的弱點,,解析并執(zhí)行任意庫函數(shù),,允許我們在沒有信息泄漏的情況下成功地攻擊加固后的應用。任何庫函數(shù)都可以被執(zhí)行,,只要它所在庫被加載進程序,。既然所有的二進制程序都依賴于C語言庫,這就表示我們的技術(shù)能執(zhí)行system()和execve()這類安全關(guān)鍵(security-critical)函數(shù),,從而允許執(zhí)行任意命令,。我們還會展示一些通過重用特定應用程序庫中的函數(shù)來進行復雜又隱秘的攻擊。這項技術(shù)非??煽壳壹軜?gòu)無關(guān),,攻擊者不需要知道版本、布局,、內(nèi)容或者其他關(guān)于庫函數(shù)不可知的信息,。 我們實現(xiàn)了自己的想法,寫成了一個稱為Leakless的原型工具。要使用Leakless,,攻擊者必須擁有目標應用的副本,,且能夠利用漏洞(即劫持控制流)。之后,,Leakless可以在沒有信息泄漏的情況下自動地創(chuàng)建利用過程,,并且調(diào)用攻擊者感興趣的關(guān)鍵庫函數(shù)。 為了評估我們技術(shù)的影響,,我們對幾個不同的Linux(以及FreeBSD)發(fā)行版進行了調(diào)研,,發(fā)現(xiàn)其中大部分的二進制程序容易被Leakless的攻擊所影響(如果目標程序有內(nèi)存破壞漏洞的話)。我們還審查了多種C語言庫的動態(tài)裝載器實現(xiàn),,發(fā)現(xiàn)其中大多數(shù)也是容易被Leakless的技術(shù)影響的,。除此之外,我們展示了一種流行的緩解技術(shù),,RELocation Read-Only (RELRO) 重定位只讀,,它能夠保護庫函數(shù)的調(diào)用不受攻擊者重定向的影響。然而它也被Leakless完全地繞過了,。最后,,我們比較了Leakless與類似的ROP編譯器產(chǎn)生的ROP鏈的長度。Leakless產(chǎn)生的ROP鏈的長度顯著地短于現(xiàn)有技術(shù)產(chǎn)生的ROP鏈,。就像我們展示的那樣,,與傳統(tǒng)ROP編譯器相比Leakless能夠?qū)崿F(xiàn)更加廣泛的利用。 總的來說,,我們作出了如下貢獻: – 我們開發(fā)了一個新的,、架構(gòu)與平臺無關(guān)的攻擊,使用基于ELF,、支持動態(tài)裝載的系統(tǒng)的固有功能,,使得攻擊者能夠在不做信息泄漏的情況下,執(zhí)行任意庫函數(shù),。 – 我們詳述了實現(xiàn)自己的系統(tǒng)的過程中,,面對不同動態(tài)裝載器實現(xiàn)和多種緩解措施(包括RELRO)的挑戰(zhàn),并最終克服了它們,。 – 最后,,我們進行了一次深入的評估,包括以前復雜的利用因使用了我們的技術(shù)變得容易的案例分析,,對幾種不同動態(tài)裝載器實現(xiàn)安全性的評定,,我們的技術(shù)在不同操作系統(tǒng)配置下適用性的調(diào)研,以及Leakless在ROP鏈長度改善方面的測量,。 相關(guān)工作:內(nèi)存破壞的軍備競賽內(nèi)存破壞的軍備競賽(即防御者針對現(xiàn)有的利用技術(shù)開發(fā)對抗措施,,接著攻擊者想出新的利用技術(shù)來繞過這些措施的過程)已經(jīng)持續(xù)了幾十年。這場競賽的歷史已經(jīng)被記錄在別處 [37],這一節(jié)著眼于那些使得現(xiàn)代利用技術(shù)被拆分成2個階段的事件,,就是說,,需要攻擊者在執(zhí)行任意代碼前進行信息泄漏這一步。 早期的棧溢出利用依賴于向緩沖區(qū)中注入二進制代碼(稱為shellcode)的能力,,并需要覆蓋在棧上的一個返回地址使其指向這個緩沖區(qū),。隨后,當程序從當前函數(shù)返回時,,執(zhí)行流就會被重定向到攻擊者的shellcode,,接著攻擊者就能取得程序的控制權(quán)。 結(jié)果,,安全研究者提出了另一項減緩技術(shù):不可執(zhí)行位(the NX bit),。不可執(zhí)行位具有防止那些不該有代碼的區(qū)域(棧就是典型的這類區(qū)域)被執(zhí)行的效果,。 不可執(zhí)行位逼迫攻擊者們開始采用“代碼重用”的理念:使用程序中已經(jīng)存在的代碼(例如系統(tǒng)調(diào)用或安全關(guān)鍵(security-critical)庫函數(shù))來達到他們的目的,。在return-to-libc的利用中 [30,39] ,一個攻擊者將控制流重定向到一個敏感的libc函數(shù)(例如system()),,并給予其恰當?shù)膮?shù)來實行惡意的行為,,而不是注入shellcode。 為了對抗這項技術(shù),,一項系統(tǒng)級的加固措施,,稱為地址空間布局隨機化ASLR被開發(fā)出來。一旦ASLR起作用,,攻擊者將無法知道庫的位置,。實際上,程序的內(nèi)存布局(庫被加載的位置,,棧的位置以及堆的位置)每次執(zhí)行都是隨機的,。因此,攻擊者不知道將控制流重定向到哪里才能執(zhí)行特定函數(shù),。更糟糕的是,,即使是攻擊者能夠確定這些信息,他仍然不知道特定函數(shù)在庫中的位置,,除非他取得庫文件的一份副本,。結(jié)果,攻擊者常常需要泄漏庫本身的內(nèi)容并解析代碼來確定關(guān)鍵函數(shù)的位置,。為了泄漏庫,,攻擊者需要重用一些程序代碼段里的小塊代碼(稱為gadgets)來泄漏內(nèi)存位置。這些gadgets可以通過將其地址寫在棧上并連續(xù)地執(zhí)行返回(ret)指令被組合使用,。所以,,這項技術(shù)被稱作“面向返回編程”(Return Oriented Programming (ROP)). ROP是攻擊者的一個強有力的工具。實際上,在許多二進制程序中發(fā)現(xiàn)的ROP Gadgets是“圖靈完全”集合,,借助ROP編譯器能夠完成利用的任務,。然而,由于其普適性的需要,,ROP編譯器傾向于生成依賴于具體漏洞細節(jié)的長ROP鏈,,它們“太長而無用” [22].在這之后,我們將展示Leakless生成的相對短的ROP鏈,,并且依賴于存在的緩解措施,,只需要很少的gadgets。此外,,Leakless在沒有圖靈完全的Gadgets的集合的情況下也能發(fā)揮作用,。 在真實世界的利用中,攻擊者往往使用一個信息泄漏攻擊來泄露庫的地址或內(nèi)容,,然后使用這些信息來計算安全關(guān)鍵(security-critical)函數(shù)(例如system())的地址,,最后發(fā)送第二段攻擊載荷(payload)到漏洞應用來重定向控制流到想要的函數(shù)。 實際上,,我們觀察到尋找特定庫函數(shù)的目標已經(jīng)被動態(tài)裝載器實現(xiàn)了,這是一個能夠進行符號解析(即確定庫函數(shù)地址)的操作系統(tǒng)組件,。所以,,我們意識到可以使用動態(tài)裝載器來略過信息泄漏這一步并巧妙地進行利用。因為我們的攻擊不需要信息泄漏的步驟,,所以我們稱它為Leakless. 使用動態(tài)連接器的觀念已經(jīng)在一些return-to-libc的攻擊中作為利用過程的一部分被簡要地探究過 [15,21,30] 。然而,,現(xiàn)有的技術(shù)非常依賴現(xiàn)有狀況,,依賴平臺,需要2個階段,,或者易受到現(xiàn)有的緩解技術(shù)(例如RELRO)的影響,,這一影響我們將在后面討論。而Leakless,,作為只有1個階段,平臺無關(guān),,具有普適性的技術(shù),,在這樣的緩解技術(shù)面前仍然能夠發(fā)揮作用,。 在下一節(jié)中,我們將闡述動態(tài)裝載器是如何工作的,,然后將會展示如何活用它的功能來進行我們的攻擊,。 動態(tài)裝載器動態(tài)裝載器是一個用戶執(zhí)行環(huán)境的組件,,它能夠幫助在開始時加載應用需要的庫并解析庫導出的動態(tài)符號(函數(shù)和全局變量)供應用程序使用,。在這一節(jié)中,,我們將會闡述動態(tài)符號解析的過程在基于ELF的系統(tǒng)上是如何工作的 [33]. ELF是Unix類平臺(包括GNU/Linux與FreeBSD)上比較普遍的一種標準格式,,其定義獨立于任何一種特定動態(tài)裝載器實現(xiàn),。因為Leakless主要依賴與ELF標準的特性,,它也很容易應用于很多系統(tǒng),。 3.1.ELF對象一個應用由一個主要ELF二進制文件(可執(zhí)行文件)和數(shù)個動態(tài)庫構(gòu)成,,它們都是ELF格式,。每個ELF對象由多個segments組成,,每個segment則含有一個或多個sections(譯注: 以下稱sections為段). 每個段都有約定的含義,。舉個例子,.text段包含著程序的代碼,,.data段包含著它可寫的數(shù)據(jù)(例如全局變量),,而.rodata段則包含著只讀的數(shù)據(jù)(例如常量和字符串)。段的列表以數(shù)組的形式存于ELF文件的Elf_Shdr結(jié)構(gòu)體中,。 注意這里有兩種ELF結(jié)構(gòu)體的版本: 一種是32位的(例如Elf32_Rel),,一種是64位的(例如Elf64_Rel).為了簡化起見,除了在相關(guān)討論的特定案例中,,一般情況我們將忽略這些細節(jié),。 動態(tài)符號與重定位在這一節(jié)中,,我們將對ELF符號解析過程相關(guān)的數(shù)據(jù)結(jié)構(gòu)進行一個總結(jié),。圖1總體展示了這些數(shù)據(jù)結(jié)構(gòu)以及它們之間的關(guān)系。 figure 1: 在符號解析過程中相關(guān)數(shù)據(jù)結(jié)構(gòu)的關(guān)系(除去符號版本),。陰影背景部分表示只讀內(nèi)存,。 一個ELF對象可以向其它ELF對象導出符號或?qū)敕枴R粋€符號表示一個具有名稱標識的函數(shù)或者全局變量,。 每個符號都使用Elf_Sym結(jié)構(gòu)體來描述,。這個結(jié)構(gòu)體的實例是ELF中.dynsym段的組成部分,它包含以下相關(guān)的域: [st_name] 相對.dynstr段開始的偏移值,,那里有這個符號名字的字符串,。 [st_value] 如果這個符號被導出,,則存有這個導出函數(shù)的虛擬地址,否則為NULL. 這些結(jié)構(gòu)被用來解析導入的符號,。導入符號的解析需要重定位的支持,,重定位項以Elf_Rel結(jié)構(gòu)體來描述。這個結(jié)構(gòu)的實例存在于.rel.plt段(用于導入函數(shù))和.rel.dyn段中(用于導入全局變量),。在這里我們感興趣的是前者,。Elf_Rel結(jié)構(gòu)體有以下域: [r_info] 此域的高位3個字節(jié)作為一個無符號的下標,表示這個符號在.dynsym段中的位置,。 [r_offset] 解析后的符號地址被寫入內(nèi)存中的位置(絕對地址),。 當程序?qū)胍粋€正常函數(shù)時,鏈接器會在.dynstr段中包含一個函數(shù)名稱的字符串,,在.dynsym段中包含一個指向它的符號(Elf_Sym),,在.rel.plt段中包含一個指向這個符號的重定位項(Elf_Rel). 重定位的目標(即Elf_Rel結(jié)構(gòu)中的r_offset域)將會是全局偏移量表(Global Offset Table, GOT)中的一個條目。GOT表保存于.got.plt段,,由能夠解析.rel.plt段中的重定位的動態(tài)鏈接器來填寫,。 惰性符號解析因為在程序開始時就解析所有導入符號并應用重定位是一項開銷較大的操作,符號的解析將是惰性的,。在惰性符號解析中,,每個函數(shù)地址(相當于GOT中的條目)只在需要的時候才進行解析(即這個函數(shù)第一次被調(diào)用的時候)。 當一個程序需要調(diào)用導入函數(shù)時,,他將會調(diào)用過程鏈接表(Procedure Linkage Table, .plt段)中的一段特定代碼,。就像列表1展示的那樣,每個導入函數(shù)在PLT中有一段特定代碼,,其執(zhí)行無條件跳轉(zhuǎn)到相關(guān)的GOT條目,。 在符號解析結(jié)束后,GOT條目已經(jīng)包含了實際函數(shù)的地址,,所以執(zhí)行能夠無縫地進入導入的庫中。當函數(shù)返回時,,控制流返回到PLT中特定代碼的調(diào)用者位置,,故剩下的PLT代碼不會被執(zhí)行。不過,,當程序剛啟動時,,GOT條目被初始化為一個指向相關(guān)PLT代碼第2條指令的地址。這部分代碼將會將導入函數(shù)的標識(以一個Elf_Rel實例在.rel.plt段中偏移的形式)壓棧,,然后跳到.plt段開頭PLT0的代碼處,。這回,PLT0的代碼,,將GOT[1]的值壓棧并間接跳轉(zhuǎn)至GOT[2].這兩個GOT表的條目有著特殊的含義,,動態(tài)裝載器在開始時給他們填充了特殊的內(nèi)容: – GOT[1]. 一個指向內(nèi)部數(shù)據(jù)結(jié)構(gòu)的指針,,類型是link_map,在動態(tài)裝載器內(nèi)部使用,,包含了進行符號解析需要的當前ELF對象的信息,。 – GOT[2]. 一個指向動態(tài)裝載器中_dl_runtime_resolve函數(shù)的指針。 總的來說,,PLT條目只是進行了以下函數(shù)調(diào)用: _dl_runtime_resolve(link_map_obj, reloc_index) 這個函數(shù)使用link_map_obj參數(shù)來取得解析導入函數(shù)(使用reloc_index參數(shù)標識)需要的信息,,并將結(jié)果寫到正確的GOT條目中。在_dl_runtime_resolve解析完成導入符號中,,控制流就交到了那個函數(shù)手中,,使得解析的過程對調(diào)用者來說完全透明。下次PLT代碼調(diào)用時則會直接進入目標函數(shù)執(zhí)行,。 Listing 1: PLT與GOT的例子 link_map結(jié)構(gòu)體包含了動態(tài)裝載器加載ELF對象需要的全部信息,。每個link_map實例都是一條雙向鏈表的一個節(jié)點,而這個鏈表保存了所有加載的ELF對象的信息,。 符號版本ELF標準提供了一個可以導入一個特定版本符號的機能,。這個特性被用于從一個特定的庫中導入函數(shù)。例如,,使用版本標識GLIBC_2.2.5,,就可以從2.2.5版本的GNU C標準庫中導入fopen這個C標準庫函數(shù)。.gnu.version_r段保存了版本的定義,,形式是Elf_Verdef結(jié)構(gòu)體,。 一個動態(tài)符號與指向它的Elf_Verdef的關(guān)聯(lián)保存在.gnu.version段中,其中有一個Elf_Verneed結(jié)構(gòu)體組成的數(shù)組,,每個元素對應動態(tài)符號表中的一項,。這個結(jié)構(gòu)體只有一個域: 一個16位的整數(shù),表示.gnu.version_r段中的下標,。 得益于這樣的布局,,動態(tài)連接器使用Elf_Rel結(jié)構(gòu)體成員r_info中的下標同時作為.dynsym段和.gnu.version段的下標。理解這一過程非常重要,,因為Leakless之后將被它所擾,。 dynamic段和RELRO動態(tài)裝載器從.dynamic段收集所有它需要的關(guān)于ELF對象的信息。.dynamic段由Elf_Dyn結(jié)構(gòu)組成,,一個Elf_Dyn是一個鍵值對,,其中存儲了不同類型的信息。相關(guān)的條目已經(jīng)在表1中展示,,它們保存著特定段的絕對地址,。有一個例外是DT_DEBUG條目,它保存的動態(tài)裝載器內(nèi)部數(shù)據(jù)結(jié)構(gòu)的指針,。這個條目是為了調(diào)試的需要由動態(tài)裝載器初始化的,。 Table 1: .dynamic段的條目,,d_tag是鍵,d_value是值,。 一個攻擊者如果能干擾這些值,,那將會造成安全威脅。為此,,一種稱作RELRO(重定位只讀 RELocation Read Only)保護機制被引入了動態(tài)裝載器,。RELRO有2種形式:部分和完全。 [部分RELRO] 在這種模式下,,一些段(包括.dynamic)在初始化后將會被標識為只讀,。 [完全RELRO] 除了部分RELRO,惰性解析會被禁用: 所有的導入符號將在開始時被解析,,.got.plt段會被完全初始化為目標函數(shù)的最終地址,,并被標記為只讀。此外,,既然惰性解析被禁用,,GOT[1]與GOT[2]條目將不會被初始化為之前在3.3節(jié)中提到的值。 可以看到,,RELRO顯著地增加了復雜性,,Leakless為了能在這些對抗措施下工作,必須解決這個問題(它也做到了),。 值得注意的是之前提到的link_map結(jié)構(gòu)出于內(nèi)部用途考慮,,在l_info域中保存了.dynamic段中大多數(shù)條目的指針構(gòu)成的一個數(shù)組。既然動態(tài)裝載器完全地信任這個域的內(nèi)容,,那么Leakless將有能力巧用這個結(jié)構(gòu)達成自己的目的,。 攻擊Leakless使攻擊者只用名字就能夠調(diào)用任意庫函數(shù),不需要關(guān)于內(nèi)存布局以及漏洞程序庫的信息,。為了達到這個目標,,Leakless活用了動態(tài)裝載器,強迫其解析請求的函數(shù),。由于它和內(nèi)存破壞漏洞的破壞性有著同樣的根源: 可控數(shù)據(jù)和不可控數(shù)據(jù)的混雜,,所以這樣的攻擊同樣可能。在棧溢出的攻擊案例中,,可控數(shù)據(jù)的問題就出在保存的返回地址上。對于動態(tài)裝載器來說,,可控數(shù)據(jù)就是眾多用于符號解析的數(shù)據(jù)結(jié)構(gòu),。特別地,函數(shù)的名字,,保存在.dynstr段中,,與返回地址相似: 當函數(shù)被調(diào)用,,它也指定了一個特定的執(zhí)行目標。 動態(tài)裝載器認為它接收到的參數(shù)都是值得信任的,,因為它假設這些都是直接由ELF文件提供的或者是它自己在開始時初始化的,。然而,當一個攻擊者能夠修改這些數(shù)據(jù)時,,這個假設就不成立了,。一些動態(tài)裝載器(FreeBSD)會驗證自己接收到的輸入。然而,,他們還是完全地信任控制結(jié)構(gòu),,但這些也會被Leakless輕易地破壞。 Leakless被設計用于利用一個存在的漏洞(指緩沖區(qū)溢出等),。Leakless的輸入包括可執(zhí)行ELF文件,,一組ROP Gadgets的集合,和攻擊者希望調(diào)用的庫函數(shù)名稱(典型的例子就是execve()),。有了這些信息,,Leakless輸出一段ROP的攻擊載荷(payload)能夠執(zhí)行需要的庫函數(shù),且能夠繞過多種應用在二進制文件上的加固技術(shù),。這段ROP鏈通常來說非常短: 依賴于二進制文件中的減緩技術(shù),,需要3到12次不等的寫操作。一些Leakless產(chǎn)生的輸出的例子能夠在Leakless代碼庫的文檔里找到 [17]. Leakless不需要任何關(guān)于庫地址和內(nèi)容的信息; 我們假設ASLR在所有的動態(tài)庫上啟用且不能獲得關(guān)于它們的任何知識,。然而,,我們需要假設可執(zhí)行文件不是“位置無關(guān)的”,所以它們會被加載到內(nèi)存中的特定位置,。我們在7.2節(jié)中討論了這個限制,,并且在6.2節(jié)中展示了位置無關(guān)可執(zhí)行(Position Independent Executables, PIE)文件在現(xiàn)代操作系統(tǒng)中的分布情況。 Figure 2: 攻擊圖示,。陰影背景表示只讀的內(nèi)存,,白色背景表示可寫的內(nèi)存,紅色或加粗的部分表示攻擊者偽造的數(shù)據(jù) a) 4.1節(jié)中攻擊的例子,。攻擊者能夠改寫DT_STRTAB條目的內(nèi)容,,欺騙動態(tài)裝載器使其認為.dynstr段在.bss段中,并在那里偽造一個假的字符串表,。當動態(tài)裝載器嘗試解析printf時將會使用不同的基地址來尋找函數(shù)的名稱,,最終實際會解析并執(zhí)行execve. b) 4.2節(jié)中攻擊的例子。傳遞給_dl_runtime_resolve的參數(shù)reloc_index超出了.rel.plt段并最終落在.bss段,,在那里攻擊者偽造了Elf_Rel結(jié)構(gòu),。這個重定位項指向一個就位于其后的Elf_Sym結(jié)構(gòu),而Elf_Sym結(jié)構(gòu)中的index同樣超出了.dynsym段。這樣這個符號就會包含一個相對.dynstr地址足夠大的偏移使其能夠達到這個符號之后的一段內(nèi)存,,那里保存著這個將要調(diào)用的函數(shù)的名稱,。 在多數(shù)情況下,Leakless并不依賴目標系統(tǒng)上運行的動態(tài)裝載器的實現(xiàn)和版本,,不過有些攻擊需要一些小的改動以適應不同的動態(tài)裝載器實現(xiàn),。 值得注意的是Leakless的目標,即獲得一個庫函數(shù)的地址并執(zhí)行它,,與libdl庫中的dlsym函數(shù)十分相似,。但在實際當中這個函數(shù)很少被應用程序使用,所以,,它的地址一般攻擊者也不知道,。 基礎情形就像第3節(jié)與圖1中展示的那樣,動態(tài)裝載器從.rel.plt中的Elf_Rel結(jié)構(gòu)開始工作,,順著其中的下標找到.dynsym段中對應Elf_Sym結(jié)構(gòu)的位置,,并最終使用它確定待解析符號的名稱(在.dynstr段中的一段字符串)。最簡單的調(diào)用任意函數(shù)的辦法就是使用希望的函數(shù)的名稱覆蓋字符串表中的條目,,然后再調(diào)用動態(tài)裝載器,,但這是不可能的,因為保存著動態(tài)符號字符串表的段,,即.dynstr,,是不可寫的。 然而,,動態(tài)裝載器是從.dynamic段的DT_STRTAB條目中獲得.dynstr段的地址的,,而且DT_STRTAB條目的位置是已知的,默認情況下也可寫,。這樣,,就像圖2a中展示的那樣,我們可以將這個條目的d_val域覆蓋為一個指向攻擊者控制內(nèi)存區(qū)域的指針(典型的就是.bss或.data段),。這塊內(nèi)存區(qū)域上將會包含一段字符串,,比如execve。到了這一步,,攻擊者需要選擇一個已經(jīng)存在的符號,,它的偏移在偽造的字符串表中正好指向execve的位置,接著調(diào)用其對應的符號解析重定位過程,??梢酝ㄟ^將其重定位項的偏移壓棧并跳轉(zhuǎn)到PLT0實現(xiàn)。 這種方式非常簡單,,但僅當二進制程序的.dynamic段可寫時有效,。對于使用部分或完全RELRO編譯的二進制程序,,需要使用更復雜的攻擊,。 繞過部分RELRO就像我們在3.3節(jié)中解釋的那樣,,_dl_runtime_resolve函數(shù)的第二個參數(shù)是Elf_Rel條目在重定位表(.rel.plt段)中對應當前請求函數(shù)的的偏移。動態(tài)裝載器將這個值加上.rel.plt的基地址來得到目標Elf_Rel結(jié)構(gòu)的絕對地址,。然而多數(shù)動態(tài)裝載器實現(xiàn)不去檢查重定位表的邊界,。這就表明如果一個大于.rel.plt的值傳到_dl_runtime_resolve中,裝載器將會認為特定的地址上的數(shù)據(jù)是一個Elf_Rel結(jié)構(gòu)并使用它,,即使那里已經(jīng)超出了.rel.plt段的范圍,。 就像圖2b顯示的那樣,Leakless計算一個能夠?qū)dl_runtime_resolve導向到攻擊者控制的內(nèi)存空間的下標值,。然后它制造一個Elf_Rel結(jié)構(gòu),,并填寫r_offset的值為一個可寫的內(nèi)存地址來將解析后的函數(shù)地址寫在那里。同理,,r_info的值將會是一個將動態(tài)裝載器導向到攻擊者控制內(nèi)存的下標,。Leakless會將一個偽造的Elf_Sym對象放在那個下標對應的位置,其中的st_name域,,這個值也大到足以達到攻擊者控制的內(nèi)存,。在這段內(nèi)存最后,Leakless會放置將要執(zhí)行的函數(shù)的名稱,。 總之,,Leakless將這一條符號解析過程中需要使用的結(jié)構(gòu)鏈全部都偽造了出來,完全控制了對于攻擊者控制內(nèi)存中內(nèi)容的“函數(shù)調(diào)用”過程,。在這之后,,Leakless將計算好的假Elf_Rel結(jié)構(gòu)的偏移壓棧,并調(diào)用PLT0代碼,。 然而,,這個方法會受到幾個限制。首先,,Elf_Rel的下標需要是正數(shù),,因為r_info域在ELF標準中規(guī)定是一個無符號整數(shù)。這就意味著在實際中這塊可寫的內(nèi)存空間(例如.bss段)必須是位于.dynsym段之后,。在我們的評估中,,情況總是滿足的。 另一個限制是ELF會使用在3.4節(jié)中提到的符號版本系統(tǒng),。在這種情況下,,Elf_Rel.r_info域不僅用作動態(tài)符號表中的下標,也用作符號版本表(.gnu.version段)中的下標,。通常來說,,Leakless能夠自動的滿足這些限制,除了x86-64中使用huge pages的小型二進制程序 [32]. 我們在附錄A中詳述了關(guān)于符號版本的額外限制。當這些限制不能被滿足時,,必須使用一個替代的方法,。這就需要活用動態(tài)裝載器,通過破壞其內(nèi)部數(shù)據(jù)結(jié)構(gòu)來改變動態(tài)解析的過程,。 破壞動態(tài)裝載數(shù)據(jù)我們回想起_dl_runtime_resolve的第一個參數(shù)是一個指向link_map數(shù)據(jù)類型的指針,。這個結(jié)構(gòu)體,包含了ELF可執(zhí)行文件的信息,,而且這些內(nèi)容會被動態(tài)裝載器完全地信任,。此外,Leakless可以獲得有漏洞程序的GOT表的第二個條目,,它的位置是確定已知的,。 回想3.5節(jié)中l(wèi)ink_map的結(jié)構(gòu),在l_info域中,,包含著.dynamic段所有條目指針構(gòu)成的一個數(shù)組,。這些指針就是動態(tài)鏈接器拿來定位符號解析過程中使用的對象的。就像圖3中顯示的那樣,,通過覆蓋這個數(shù)據(jù)結(jié)構(gòu)的一部分,,Leakless能夠?qū)_info域中的DT_STRTAB條目指向一個特意制造的動態(tài)條目,那里指向一個假的動態(tài)字符串表,。結(jié)果,,攻擊者就可以將攻擊簡化為4.1節(jié)中的基礎情形了。 這個技術(shù)較上一節(jié)中的技術(shù)而言有著更加廣泛的適用性,,因為它沒有特定的限制,。特別的,它對于使用huge pages的小型64位ELF同樣適用,。然而,,相比于之前只依賴于標準ELF的特性的攻擊,在這種情況(和下一節(jié)要敘述的情況)下,,我們需要假設特定glibc的結(jié)構(gòu)(link_map)布局是已知的,。每個動態(tài)裝載器有它自己的結(jié)構(gòu)實現(xiàn),故當面對不同的動態(tài)裝載器時就需要做一些小的改動,。需要注意的是link_map的布局在同一種動態(tài)裝載器的不同版本之間也可能不同,。然而,他們顯得非常穩(wěn)定,,尤其是glibc中的相關(guān)結(jié)構(gòu)從2004年起就沒變過,。 Figure 3: 4.3節(jié)中攻擊的例子。攻擊者通過解引用GOT的第二項來取得link_map結(jié)構(gòu),。在這個結(jié)構(gòu)中破壞保存DT_STRTAB指針的l_info域,。它的值被設成一個偽造的動態(tài)條目的地址,,那里指向了一個位于.bss段中的假的動態(tài)字符串表。 完全RELRO的情形Leakless可以繞過完全RELRO的保護,。 當完全RELRO應用時,,所有的重定位將在加載時完成,不會有惰性解析的過程,,并且link_map結(jié)構(gòu)的地址和在GOT中的_dl_runtime_resolve也不會被初始化,。所以,像普通技術(shù)繞過部分RELRO那樣直接獲得它們的地址是不可能的,。然而,從動態(tài)表的DT_DEBUG條目中間接恢復這兩個值仍然是可能的,。DT_DEBUG條目的值是動態(tài)裝載器在加載時設置好的,,它指向一個r_debug類型的數(shù)據(jù)結(jié)構(gòu)。這個數(shù)據(jù)結(jié)構(gòu)保存著調(diào)試器用來標識動態(tài)裝載器的基地址并攔截相應事件需要的信息,。此外,,這個結(jié)構(gòu)的r_map域保存著一個指向link_map鏈表頭部的指針。 Leakless破壞了這個鏈表中描述ELF可執(zhí)行文件的第一個節(jié)點,,使得用于保存DT_STRTAB的l_info條目指向一個假的動態(tài)字符串表的指針,。具體情形如圖4所示。 Figure 4: 4.4節(jié)中的攻擊圖示,。陰影背景表示只讀的內(nèi)存,,白色的背景表示可寫的內(nèi)存,紅色與加粗的部分表示攻擊者偽造的數(shù)據(jù),。攻擊者使用DT_DEBUG這個動態(tài)條目來獲取r_debug結(jié)構(gòu),,接著,解引用r_map域從而得到主程序的link_map結(jié)構(gòu),,然后像第3節(jié)中展示的那樣破壞l_info[DT_STRTAB],。 因為完全RELRO的緣故.got.plt是只讀的,攻擊者需要偽造一個重定位項,。為此,,他破壞l_info[DT_JMPREL]使其指向一個假的動態(tài)條目,而這個動態(tài)條目則指向一個重定位項,。這個重定位項引用了已經(jīng)存在的printf符號,,但r_offset則指向一塊可寫的內(nèi)存區(qū)域。 接著攻擊者同樣需要恢復_dl_runtime_resolve函數(shù)的指針,,因為完全RELRO現(xiàn)在它在主程序的GOT中已經(jīng)不存在了,,所以他解引用l_info域中的第一個link_map結(jié)構(gòu)取得描述第一個共享庫的link_map,而這個共享庫是不被完全RELRO保護的,。攻擊者通過l_info[DT_PLTGOT]域來得到對應的動態(tài)條目(右側(cè)的.dynamic),,接著是.plt.got段(總是在右側(cè)),,其中的第二個條目里就有_dl_runtime_resolve的地址。 在這之后,,Leakless必須調(diào)用_dl_runtime_resolve,,將剛剛破壞的link_map結(jié)構(gòu)作為第一個參數(shù)傳過去,并將一個新的.dynsym偏移作為第二個參數(shù)傳過去,。然而,,就像之前提到的那樣,_dl_runtime_resolve因為完全RELRO的緣故在GOT中已經(jīng)不存在了,。所以,,Leakless必須在另一個ELF對象的GOT表中找到它的地址,換句話說,,就是一個被程序使用而沒有完全RELRO保護的庫,。在大多數(shù)情況下,只有ELF可執(zhí)行文件本身是被完全RELRO保護的,,但庫并不會,。這是因為RELRO是在犧牲性能的前提下,用來加固一些被認為比較“有風險”的特定應用程序的,。在共享庫上應用完全RELRO將會影響所有使用這個庫應用程序的性能,,所以庫文件一般是不受保護的。因為各個庫在鏈表上的順序是確定的,,Leakless可以解引用link_map中的l_next項來得到不被完全RELRO保護的庫文件的link_map,,解引用它的l_info項得到對應的DT_PLTGOT動態(tài)條目的值,再解引用它的值(即這個庫GOT的基地址),,就可以從GOT中獲得_dl_runtime_resolve的地址了,。 Leakless接下來必須要克服以下問題: _dl_runtime_resolve不僅僅會調(diào)用目標函數(shù),還會嘗試將它的地址寫到正確的GOT項中,。如果這件事發(fā)生,,程序就會崩潰,因為完全RELRO保護下GOT是不可寫的,。我們可以通過偽造link_map中的DT_JMPREL動態(tài)條目來繞過這個問題,。原本DT_JMPREL指向.rel.dyn段,Leakless將其指向攻擊者控制的一塊內(nèi)存區(qū)域,,那里寫有一個Elf_Rel結(jié)構(gòu),,且其r_offset域指向一塊可寫的內(nèi)存區(qū)域,其r_info指向我們的目標符號,。所以,,當一個庫被解析的時候,它的地址將會被寫到一個可寫的位置,,程序就不會崩潰了,,而且請求的函數(shù)也將會被執(zhí)行,。 實現(xiàn)Leakless將會分析二進制文件以確定它的技術(shù)是否適用,接著制造必要的數(shù)據(jù),,然后生成一段ROP鏈來實現(xiàn)所選技術(shù),。至于發(fā)現(xiàn)最初始的漏洞以及自動提取有用的gadgets那并非我們的工作,它們已經(jīng)在很多著作中被很好地研究和實現(xiàn)過了 [6,16,19,20,34,38].我們將Leakless設計成與多種gadget發(fā)現(xiàn)技術(shù)兼容,,并實現(xiàn)了一個手動的后端(用戶可以給程序提供gadgets),,另外還有一個使用ROPC [22] 的后端。ROPC是一個以Q [34] 提出的方法為基礎實現(xiàn)的一個自動化ROP編譯器的原型,。 我們還開發(fā)了一個小型的測試套件,,由一個具有基于棧的緩沖區(qū)溢出的C程序組成,同時提供無保護,,部分RELRO和完全RELRO的版本,。這個測試套件可以在x86, x86-64, arm架構(gòu)的GNU/Linux系統(tǒng)和x86-64架構(gòu)的FreeBSD系統(tǒng)上運行。 需要的GadgetsLeakless包含了4種用于不同加固措施的利用技術(shù),。應用這些不同的技術(shù)需要提供不同的gadgets。表2是對這些gadgets類型的一個總結(jié),。write_memory gadget主要用于在已知地址偽造數(shù)據(jù)結(jié)構(gòu),,deref_write gadget用于遍歷和破壞數(shù)據(jù)結(jié)構(gòu)(尤其是link_map).deref_save和copy_to_stack gadgets是用在完全RELRO的情況中的。前者的目的是將link_map和_dl_runtime_resolve的地址保存在一個已知位置,,而后者的則是用來將link_map和重定位項的下標放到棧上然后調(diào)用_dl_runtime_resolve,,因為使用PLT0已經(jīng)不可行。 Table 2: 多種方法需要的Gadgets,?!盨ignature”列代表gadget的名字和它接受的參數(shù),”Implementation”代表gadget行為的類C偽代碼,。最后四列指示了某個gadget是否在第4節(jié)里對應的方法中需要,。”N”表示沒有RELRO,,”P”表示部分RELRO,,”H”表示部分RELRO且為使用huge pages的小型64位程序,”F”則表示完全RELRO,。 對于感興趣的讀者,,我們提供了Leakless在兩組不同的緩解技術(shù)保護下進行利用的深度樣例,放在Leakless代碼庫的文檔中 [17]. 評估我們使用4種方法對Leakless進行了評估,。首先我們確定了我們的技術(shù)對于不同動態(tài)裝載器實現(xiàn)的適用性,。接著分析了多個流行的GNU/Linux以及BSD的發(fā)行版(Ubuntu, Debian, Fedora和FreeBSD)中的二進制文件,從而確定易受我們攻擊影響的二進制文件所占的比率,。然后我們將Leakless應用在對真實世界中Wireshark的一個有漏洞版本的利用中,,以及一個針對Pidgin的更加復雜的攻擊中,。最后我們使用一個圖靈完全的ROP編譯器來實現(xiàn)Leakless的方法和兩個以前使用的技術(shù),并比較他們生成出的鏈的大小,。 動態(tài)裝載器為了展示Leakless的普適性,,尤其是針對不同的基于ELF的平臺,我們調(diào)查了幾種動態(tài)裝載器的實現(xiàn),。特別地,,我們發(fā)現(xiàn)GNU C標準庫(也就是在GUN/Linux發(fā)行版中廣泛使用的glibc)的動態(tài)裝載部分,其他一些Linux實現(xiàn)例如dietlibc, uClibc和newlib(在嵌入式系統(tǒng)中廣泛使用),,以及OpenBSD和NetBSD的實現(xiàn)都含有可以被Leakless利用的漏洞,。另一種嵌入式庫,musl,,則不會受到我們方法的影響因為它不支持惰性裝載,。Bionic,Android中使用的C標準庫,,同樣不可利用因為它只支持PIE的二進制文件,。最有趣的例子,不同于所有我們分析的裝載器,,是FreeBSD的實現(xiàn),。實際上,它是唯一一個會對傳進_dl_runtime_resolve的參數(shù)進行邊界檢查的,。所有其他的裝載器完全信任傳入的參數(shù),。不僅如此,所有被分析的裝載器都完全地信任控制結(jié)構(gòu),,而Leakless會在多數(shù)攻擊中破壞這個結(jié)構(gòu),。 總結(jié)來說,,在我們分析的裝載器中,,只有2個在設計上對Leakless是免疫的: musl, 它不支持惰性符號解析; 以及bionic, 它只支持PIE可執(zhí)行文件,。此外,,因為FreeBSD的動態(tài)裝載器會進行邊界檢查,,4.2節(jié)中的技術(shù)已經(jīng)不適用了,。不過其他的技術(shù)還是可以起效,。 操作系統(tǒng)調(diào)研為了能明白Leakless對真實世界的操作系統(tǒng)的影響,,我們對幾個Linux和BSD發(fā)行版中默認安裝的二進制程序進行了一次調(diào)研,。特別地,,我們檢查了所有在/sbin,/bin,/usr/bin和/usr/bin目錄下的程序,并將它們按照Leakless技術(shù)的適用性進行分類,。我們考慮的發(fā)行版有Ubuntu 14.10, Debian Wheezy, Fedora 20, 和FreeBSD 10,。我們同時使用了這些系統(tǒng)的x86和x86-64的版本。在Ubuntu和Debian上,,我們另外安裝了LAMP(Linux, Apache, MySQL, PHP)棧作為模擬一個典型服務器部署,、配置的嘗試,。 我們將程序分為了以下5類: [未保護] 此類包括沒有啟用RELRO或PIE的程序。對于這些程序,,Leakless可以應用在4.1節(jié)中介紹的基礎技術(shù),。 [部分RELRO] 開啟了部分RELRO的程序,但是沒有開啟PIE,。在這種情況下,,Leakless可以應用4.2節(jié)中介紹的技術(shù)。 [部分RELRO(huge pages)] 開啟了部分RELRO,,且使用huge pages的小型程序,,它們需要Leakless使用4.3節(jié)介紹的技術(shù)。 [完全RELRO] 開啟了完全RELRO的程序,,Leakless需要使用4.4節(jié)展示的技術(shù),。 [不受影響] 最后是使用PIE的程序,它們不受Leakless的影響(更多的討論將在7.2中進行) 這項調(diào)研的結(jié)果在進行歸一化后,,如圖5所示,。我們能夠確定,在Ubuntu中,,84%的程序受到至少一項技術(shù)的影響,,另有16%被PIE技術(shù)保護。在Debian中,,Leakless可以應用在86%的程序中。Fedora則含有76%的易受影響程序,。有趣的是,,F(xiàn)reeBSD沒有程序開啟RELRO和PIE,所以100%的程序都會受到Leakless影響,。 另外,,我們還針對這些系統(tǒng)中的共享庫進行了一次調(diào)研。我們發(fā)現(xiàn),,平均來說,,只有11%的庫啟用了完全RELRO的保護。這對Leakless來說是個好消息: 對于一個給定的程序,,能夠找到加載的沒有完全RELRO保護的庫的概率極其地高,,并且即使一個漏洞程序使用了RELRO,Leakless可以應用它的完全RELRO攻擊來繞過,。這樣一來,,RELRO作為一項緩解措施的作用基本已經(jīng)微乎其微,除非它在系統(tǒng)中大范圍使用,。 Figure 5: 目標發(fā)行版中默認安裝程序的分類,。被標識為未保護,,部分RELRO,部分RELRO HP和完全RELRO分別需要應用4.1, 4.2, 4.3, 4.4節(jié)中的攻擊技術(shù),。而對于不受影響的程序,,Leakless的方法不適用。 案例研究: Wireshark我們進行了一項將Leakless應用在一個不會與攻擊者有直接聯(lián)系的程序上的案例研究,。換句話說,,利用必須一擊完成,并且沒有地址布局信息以及庫的內(nèi)容信息,。 我們選擇了一個近期(2014年4月)的漏洞 [7],,一個在Wireshark 1.8.0到1.8.13以及1.10.0到1.10.6中MPEG協(xié)議解析器的棧緩沖區(qū)溢出漏洞。我們在用部分RELRO與完全RELRO編譯的Wireshark 1.8.2上進行這項實驗,。二者都是在x86-64的Debian Wheezy系統(tǒng)上編譯,,使用GNU C語言庫,并且沒有其它類似PIE和stack canaries的防護,。 我們使用Leakless的手動后端來確定需要的gadgets以構(gòu)建4種必要的原始功能(在5.1節(jié)中描述): write_memory, deref_write, deref_save 和 copy_to_stack. 在Wireshark的例子中,,找到能夠滿足這些原始功能的gadgets是非常容易的。 Leakless能夠使用4.2節(jié)和4.4節(jié)的技術(shù)構(gòu)建一個一擊完成(one-shot)的利用,。在兩種情況下,,它都能夠使動態(tài)裝載器調(diào)用glibc中的execve函數(shù)來啟動一個我們選擇的程序。 案例研究: Pidgin我們同樣將Leakless應用在Pidgin上來實行更加復雜的利用,,這是一個流行的多協(xié)議即時消息客戶端,。而且,我們想在不進行任何異常系統(tǒng)調(diào)用(例如 execve(“/bin/sh”),,這可能觸發(fā)入侵檢測系統(tǒng)報警)的情況下進行一次惡意操作,。我們使用了x86架構(gòu)下開啟RELRO編譯的Pidgin 2.10.7版本。 為了達到這個目的,,我們進行了一個特殊設計的利用,,使其假裝自己進入應用邏輯里合法的函數(shù)中: 通過一個代理打開隧道連接。這種攻擊的思想是一個即時通訊服務的提供者利用像CVE-2013-6487 [14] 這樣的漏洞來獲得代碼執(zhí)行,,并且使用Pidgin的全局代理設置,,將所有的即時通訊流量重定向到一個第三方的服務器上,從而造成聊天竊聽,。 一旦我們確定了必要的gadgets來使用Leakless對抗完全RELRO保護,,調(diào)用libpurple.so(應用程序核心邏輯所在)中的函數(shù)就非常容易了,接著進行下面列表2中的C代碼的等價操作 Listing 2: Pidgin攻擊 有趣的是,,有些庫提供的函數(shù)沒有被導入到Pidgin程序中,,這樣在沒有Leakless的情況下僅使用一個階段的載荷完成攻擊將非常具有挑戰(zhàn)性。 ROP鏈的長度比較為了證明Leakless方法的效率,我們將其與現(xiàn)有的允許攻擊者調(diào)用任意庫函數(shù)的兩項技術(shù)進行了比較,。第一項技術(shù)首先從.plt.got段向后掃描庫,,直到發(fā)現(xiàn)ELF的頭部,隨后向前掃描發(fā)現(xiàn)攻擊者想要調(diào)用的函數(shù)的特征,。這種方法是可行的,,但并不十分可靠,因為一個庫的不同版本(或?qū)崿F(xiàn))可能無法通過單一的特征就確定,。第二項技術(shù)要可靠一些,,因為它實現(xiàn)了一個完整的符號解析的過程,就像動態(tài)裝載器一樣,。 我們實現(xiàn)了這兩種方法,,使用一種x86架構(gòu)上基于Q [34]的圖靈完全ROP編譯器,稱為ROPC [22],。我們在部分RELRO與完全RELRO兩種情況下與Leakless的ROPC后端進行了比較,。為了完整起見,我們同樣包括了Leakless的手動后端,,使用用戶指定的gadgets. 事實上,,ROP鏈長度是十分重要的,一個漏洞往往伴隨著固定長度的載荷限制,。為了測量Leakless的ROP鏈長度的影響,,我們收集了Metasploit Framework [31] (一個可以自動進行多種軟件已知漏洞利用的工具)中所有漏洞的載荷長度限制。我們發(fā)現(xiàn)1303個漏洞中的946個有最大載荷長度限制,,它們的平均最大載荷長度為1332字節(jié),。為了展示自動生成復雜利用的可行性的增長,我們在圖中增加了每種技術(shù)能夠自動生成足夠短的ROP鏈來利用的Metasploit漏洞占其總漏洞的百分比,。 結(jié)果,,在為ROPC的測試程序生成的ROP鏈長度方面和對Metasploit的漏洞利用的可行性方面,這些技術(shù)的表現(xiàn)如表3所示,。Leakless全面超過了現(xiàn)有的技術(shù),不僅是在進行初始調(diào)用的ROP鏈絕對長度上,,在每次額外調(diào)用上的開銷也更小,。這使它在進行像6.4節(jié)中的復雜攻擊中更加有效。 Table 3: ROPC為6.5節(jié)中的每種技術(shù)生成的ROP鏈的長度,,以及Leakless手動后端生成的結(jié)果(*),。第2列代表從攻擊準備到第一次調(diào)用需要的字節(jié)數(shù),第3列則表示后來的調(diào)用每次需要的字節(jié)數(shù),。最后,,第4列顯示了按照其第一次調(diào)用的大小生成的ROP鏈能夠利用的Metasploit中的漏洞比率。 討論在這節(jié)中,我們將討論與Leakless有關(guān)的幾個方面: 為什么它提供給攻擊者的能力很有價值,,在什么情況下它最適合,,它的局限性在哪里,以及如何緩解其帶來的影響,。 Leakless的應用Leakless作為漏洞利用開發(fā)者軍械庫中的一件強力工具,,主要能夠在三個領(lǐng)域幫助他們: 功能重用,一擊利用,,以及ROP鏈的縮短,。 [一擊利用] 所有利用都可以使用Leakless來簡化,我們設計它的目標是使得那些需要信息泄漏漏洞的利用在無法進行信息泄漏時仍然可行,。這類程序中有一大部分都是文件格式解析器,。 解析文件格式的代碼極其復雜,正因如此,,當遇到不可信的輸入時,,就有內(nèi)存破壞的傾向。有很多這樣的例子: 過去10年解析圖片的libpng庫有27條CVE記錄 [10], libtiff則有53條 [11],。復雜格式的解析器甚至更加糟糕: 過去5年多媒體庫ffmpeg已經(jīng)累計了170條CVE記錄 [9].不僅僅局限于多媒體類的庫,。Wireshark, 一個網(wǎng)絡包分析器,,有285條CVE記錄,,多數(shù)是因為網(wǎng)絡協(xié)議分析插件的漏洞 [12]. 這些庫,以及和它們類似的其它庫,,往往是離線工作的,。用戶可以先下載一個媒體或者PCAP文件,然后使用這些庫來解析,。當漏洞觸發(fā)的時候,,攻擊者無法與受害者建立一個直接連接來接受泄漏的信息或者發(fā)送額外的攻擊載荷。不僅如此,,大部分這種格式都是被動的,,也就是說(不像PDF),它們不含有攻擊者模擬2個階段利用需要的腳本,。結(jié)果,,即使這些庫是有漏洞的,對它們的利用也是非常困難,,不可靠,,或者完全不可實行。通過避免信息泄漏的步驟,,Leakless能夠使得這些利用能更簡單,,可靠,,可行。 [功能重用] Leakless讓攻擊者能夠調(diào)用程序加載的庫中的任意函數(shù),。事實上,,漏洞程序不需要真正”導入”這些函數(shù),它只需要對這些庫進行鏈接即可(即可以調(diào)用庫中的其它函數(shù)),。這就帶來了一些便利,。 首先,被大部分程序鏈接的C標準庫,,包含了幾乎所有系統(tǒng)調(diào)用的封裝函數(shù)(例如read(),、execve()等等)。這就意味著Leakless能夠進行任何系統(tǒng)調(diào)用,,并且可以在沒有系統(tǒng)調(diào)用gadget的情況下使用一條短小的ROP鏈完成,。 不僅如此,就像6.4節(jié)中說的,,Leakless能夠輕易地重用程序邏輯中現(xiàn)有的功能,。這很重要,原因有二,。 第一個原因,,通過將利用偽裝成程序正常的行為能夠幫助攻擊者進行更加隱秘的攻擊。當標準利用的套路被諸如seccomp [2], AppArmor [1] 或SELinux [25]這種保護機制封鎖時,,這就是決定性的,。 第二個原因,根據(jù)攻擊者的具體目標,,重用函數(shù)功能往往比執(zhí)行任意命令更好,。除了在Pidgin的案例研究中的攻擊之外,攻擊者可以悄悄地在Firefox瀏覽器中啟用不安全的密碼套件或者SSL的不安全版本,,僅僅需要一個SSL_CipherPrefSetDefault調(diào)用 [24]. [縮短ROP鏈] 就像6.5節(jié)說的,,Leakless能夠產(chǎn)生比現(xiàn)有技術(shù)更短的ROP鏈。實際上,,在很多場合,,Leakless通過產(chǎn)生小于1KB的ROP鏈就能完成任意函數(shù)的調(diào)用。在很多漏洞都有輸入長度的限制的情況下,,這是很重要的結(jié)果,。例如,我們在Pidgin的案例研究中利用的漏洞允許的最大ROP鏈長度為1KB,。普通的ROP編譯技術(shù)無法自動生成這個漏洞的載荷,而Leakless卻可以通過自動生成不超過長度限制的ROP鏈來調(diào)用任意函數(shù),。 限制Leakless最大的限制就是在沒有信息泄漏的情況下無法處理位置無關(guān)可執(zhí)行文件(Position Independent Executables, PIEs),。這是使用ROP的技術(shù)的通病,,因為ROP鏈中的gadgets的絕對地址必須確定。除此之外,,在沒有程序基地址的情況下,,Leakless也無法定位它要破壞的動態(tài)裝載器結(jié)構(gòu)。 當遇到PIE的程序時,,Leakless需要攻擊者能夠提供應用程序的基地址,,這應該能夠通過一個信息泄漏漏洞得到(或者使用BROP技術(shù) [5])。這就打破了Leakless不需要信息泄漏就可以進行操作的能力,。不過Leakless仍然是進行利用最方便的方式,,因為不需要泄漏庫的地址和內(nèi)容。除此之外,,依據(jù)具體情況,,僅僅泄漏程序的地址或許比泄漏整個庫的內(nèi)容更加可行。與其它需要后者的技術(shù)不同,,Leakless只需要前者,。 實際當中,PIE因為關(guān)系到性能開銷并不太常見,。有測試結(jié)果顯示PIE在x86處理器上的額外開銷平均為10%,,而在x86-64處理器上,得益于相對指令指針(instruction-pointer-relative)的尋址方式,,平均有3.6%的額外開銷 [28] ,。 因為PIE相關(guān)的額外開銷開銷太大,大部分發(fā)行版只將那些“有風險”的程序上開啟PIE,。例如,,資料顯示,Ubuntu的官方支持程序包(即在主要倉庫中的包)中只有27個開啟了PIE,,沒有開啟的則有超過27000個包 [40],。正如6.1節(jié)中說的,PIE可執(zhí)行程序只占我們調(diào)研過的所有系統(tǒng)中很小一部分,。 對抗措施有多種能夠?qū)筁eakless的方法,,不過它們都有缺點。在本節(jié)中我們將會分析最適用的幾種方法,。 [位置無關(guān)可執(zhí)行] 一個快速的對抗措施就是讓所有系統(tǒng)中的可執(zhí)行文件都是位置無關(guān)的,。這雖然會封鎖Leakless的自動操作(就像7.2節(jié)中討論的那樣),但當存在任何信息泄漏時Leakless的技術(shù)還是可以應用的,。為此,,再加上PIE相關(guān)的性能開銷,我們認為這節(jié)中討論的其他對抗措施比它更為適用,。 [禁用惰性裝載] 當LD_BIND_NOW環(huán)境變量被設置時,,動態(tài)裝載器將會完全地禁用惰性裝載,。這就意味著,所有為了程序和其依賴庫的導入將在程序開始時被解析,。由此帶來的副作用就是_dl_runtime_resolve函數(shù)的地址不會被裝載到任何庫的GOT中,,Leakless就無法工作了。這就相當于在整個系統(tǒng)上應用了完全RELRO,。所以,,他也同樣會帶來不可忽略的性能上的額外開銷。 [禁用DT_DEBUG] 最后,,Leakless將會使用DT_DEBUG這個動態(tài)條目來繞過完全RELRO,。而這個條目是調(diào)試器用來攔截與裝載有關(guān)事件使用的。現(xiàn)在,,這個域總是會被加載,,為Leakless的完全RELRO繞過敞開大門。為了關(guān)閉這個坑,,可以對動態(tài)裝載器進行修改讓它只在有調(diào)試器存在或有明確定義的環(huán)境變量存在時才去初始化這個值,。 [對裝載器控制結(jié)構(gòu)更好的保護] Leakless非常依賴于“動態(tài)裝載器的控制結(jié)構(gòu)很好獲得”這一事實,并且它們的位置也是眾所周知的,。與將它們裝載到一個已知位置相比,,把這些結(jié)構(gòu)更好地保護或者隱藏在內(nèi)存中會是個好主意。例如,,在 [29] 中說的那樣,,對于這些結(jié)構(gòu)以及任何會提供符號解析控制數(shù)據(jù)的段,應當在初始化后將它們標為只讀,。這樣的改進能夠徹底抹消Leakless破壞這些結(jié)構(gòu)的能力,,并能防止將控制流重定向到敏感函數(shù)的這類攻擊。 此外,,修改裝載的過程讓它使用一張表來記錄link_map結(jié)構(gòu),,并給_dl_runtime_resolve在表中賦予一個下標,而不是使用一個直接的指針,,這樣能夠打破Leakless對完全RELRO的繞過,。然而,這種改變將會破壞之前編譯的任何二進制程序的兼容性,。 [隔離動態(tài)裝載器] 將動態(tài)裝載器從目標程序的地址空間中隔離是一個有效的對抗措施,。舉例來說,諾基亞的塞班操作系統(tǒng),,其使用了微內(nèi)核結(jié)構(gòu),,動態(tài)裝載器是在一個單獨的進程中實現(xiàn)的,這個進程作為一個系統(tǒng)服務器(system server)與內(nèi)核交互,。這就保證了動態(tài)裝載器的控制結(jié)構(gòu)不會被程序破壞,。所以,,這實際上已經(jīng)使得Leakless失效了。然而,,這種對抗措施將會給程序的整體性能帶來相當大的影響,因為進程間通信(Inter-Process Communication, IPC)也需要額外開銷,。 總體來說,,這些緩解措施要么帶來運行時的性能開銷(PIE或裝載器隔離),要么帶來裝載時的性能開銷(非惰性裝載和系統(tǒng)范圍的RELRO),,要么需要修改裝載的過程(DT_DEBUG的禁用以及裝載器控制結(jié)構(gòu)隱藏).從長遠來看,,我們相信一個考慮安全的對動態(tài)裝載器的重新設計將會讓我們都受益匪淺。在短期內(nèi),,也有幾種針對Leakless可選的保護方案,,不過都會有性能損失。 結(jié)論在這篇文章中,,我們展示了Leakless這種新技術(shù),,能夠使用動態(tài)裝載器的功能來使得攻擊者在利用中使用任意的、安全關(guān)鍵(security-critical)的庫函數(shù),,而不需要知道這些函數(shù)在程序內(nèi)存中的位置,。而在以前,實現(xiàn)這種利用則需要先進行信息泄漏這一步驟,。 因為Leakless使用的是ELF二進制格式規(guī)定的特性,,所以攻擊能夠跨架構(gòu),跨操作系統(tǒng),,跨動態(tài)裝載器來進行,。此外,我們展示了我們的技術(shù)能夠繞過像RELRO這種保護動態(tài)解析過程中重要控制結(jié)構(gòu)的加固方案,。最后,我們提出了幾種對抗Leakless的措施,,并討論了它們的優(yōu)缺點,。 參考文獻[1] AppArmor. http://wiki./. [2] A. Arcangeli. seccomp. https://www./doc/Documentation/prctl/seccomp_filter.txt. [3] A. Baratloo, N. Singh, and T. K. Tsai. Transparent Run-Time Defense Against Stack-Smashing Attacks. In USENIX Annual Technical Conference, General Track, pages 251–262, 2000. [4] M. Bauer. Paranoid penguin: an introduction to Novell AppArmor. Linux Journal, 2006(148):13, 2006. [5] A. Bittau, A. Belay, A. Mashtizadeh, D. Mazieres, and D. Boneh. Hacking blind. In Proceedings of the 35th IEEE Symposium on Security and Privacy, 2014. [6] S. K. Cha, T. Avgerinos, A. Rebert, and D. Brumley. Unleashing mayhem on binary code. In Security and Privacy (SP), 2012 IEEE Symposium on, pages 380–394. IEEE, 2012. [7] Common Vulnerabilities and Exposures. CVE-2014-2299. http://cve./cgi-bin/cvename.cgi?name=CVE-2014-2299. [8] C. Cowan, P. Wagle, C. Pu, S. Beattie, and J. Walpole. Buffer overflows: Attacks and defenses for the vulnerability of the decade. In DARPA Information Survivability Conference and Exposition, 2000. DISCEX’00. Proceedings, volume 2, pages 119–129. IEEE, 2000. [9] CVEDetails.com. ffmpeg: CVE security vulnerabilities. http://www./product/6315/Ffmpeg-Ffmpeg.html. [10] CVEDetails.com. Libpng: Security Vulnerabilities. http://www./vendor/7294/Libpng.html. [11] CVEDetails.com. Libtiff: CVE security vulnerabilities. http://www./product/3881/Libtiff-Libtiff.html. [12] CVEDetails.com. Wireshark: CVE security vulnerabilities. http://www./product/8292/Wireshark-Wireshark.html. [13] CWE. CWE/SANS Top 25 Most Dangerous Software Errors. http://cwe./top25/. [14] N. V. Database. NVD – Detail – CVE-2013-6487. http://web.nvd./view/vuln/detail?vulnId=CVE-2013-6487. [15] A. Di Federico, A. Cama, Y. Shoshitaishvili, C. Kruegel, and G. Vigna. Leakless source code repository. https://github.com/ucsb-seclab/leakless. [16] S. Dudek. The Art Of ELF: Analysis and Exploitations. http:///1a8MeEw. [17] T. Dullien, T. Kornau, and R.-P. Weinmann. A Framework for Automated Architecture-Independent Gadget Search. In WOOT, 2010. [18] M. Fox, J. Giordano, L. Stotler, and A. Thomas. Selinux and grsecurity: A case study comparing linux security kernel enhancements. 2009. [19] I. Haller, A. Slowinska, M. Neugschwandtner, and H. Bos. Dowsing for Overflows: A Guided Fuzzer to Find Buffer Boundary Violations. In USENIX Security, pages 49–64, 2013. [20] C. Heitman and I. Arce. BARFgadgets. https://github.com/programa-stic/barf-project/tree/master/barf/tools/gadgets. [21] inaz2. ROP Illmatic: Exploring Universal ROP on glibc x86-64. http://ja./avtokyo2014/speakers#inaz2. [22] P. Kot. A Turing complete ROP compiler. https://github.com/pakt/ropc. [23] P. Menage. Cgroups. Available on-line at: http://www./kernel/Documentation/cgroups.txt, 2008. [24] Mozilla. SSL_CipherPrefSetDefault. https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/SSL_functions/sslfnc.html#_SSL_CipherPrefSetDefault_. [25] National Security Agency. Security-Enhanced Linux. http:///. [26] Nokia. Symbian OS Internals – The Loader. http://developer./community/wiki/Symbian_OS_Internals/10._The_ Loader#The_loader_server. [27] H. Orman. The Morris worm: a fifteen-year perspective. IEEE Security & Privacy, 1(5):35–43, 2003. [28] M. Payer. Too much PIE is bad for performance. 2012. https:///publications/12TRpie/gccPIE-TR120614.pdf. [29] M. Payer, T. Hartmann, and T. R. Gross. Safe Loading – A Foundation for Secure Execution of Untrusted Programs. In Proceedings of the 2012 IEEE Symposium on Security and Privacy, SP ’12, pages 18–32, Washington, DC, USA, 2012. IEEE Computer Society. [30] Phrack. Phrack – Volume 0xB, Issue 0x3a. http:///issues/58/4.html. [31] Rapid7, Inc. The Metasploit Framework. http://www./. [32] RedHat, Inc. Huge Pages and Transparent Huge Pages. https://access./documentation/en-US/Red_Hat_ Enterprise_Linux/6/html/Performance_Tuning_Guide/s-memory-transhuge.html. [33] Santa Cruz Operation. System V Application Binary Interface, 2013. http://www./developers/gabi/latest/contents.html. [34] E. J. Schwartz, T. Avgerinos, and D. Brumley. Q: Exploit Hardening Made Easy. In USENIX Security Symposium, 2011. [35] H. Shacham. The geometry of innocent flesh on the bone: Return-into-libc without function calls (on the x86). In Proceedings of the 14th ACM conference on Computer and communications security, pages 552–561. ACM, 2007. [36] R. Shapiro, S. Bratus, and S. W. Smith. ”Weird Machines” in ELF: A Spotlight on the Underappreciated Metadata. In Proceedings of the 7th USENIX Conference on Offensive Technologies, WOOT’13, pages 11–11, Berkeley, CA, USA, 2013. USENIX Association. [37] L. Szekeres, M. Payer, T. Wei, and D. Song. SoK: Eternal war in memory. In Security and Privacy (SP), 2013 IEEE Symposium on, pages 48–62. IEEE, 2013. [38] The Avalanche Project. Avalange – a dynamic defect detection tool. https://code.google.com/p/avalanche/. [39] M. Tran, M. Etheridge, T. Bletsch, X. Jiang, V. Freeh, and P. Ning. On the Expressiveness of Return-into-libc Attacks. In Proceedings of the 14th International Conference on Recent Advances in Intrusion Detection, RAID’11, pages 121–141, Berlin, Heidelberg, 2011. Springer-Verlag. [40] Ubuntu. Ubuntu Wiki – Security/Features. https://wiki./Security/Features#Built_as_PIE. [41] R. N. Watson, J. Anderson, B. Laurie, and K. Kenn-away. Capsicum: Practical Capabilities for UNIX. In USENIX Security Symposium, pages 29–46, 2010. 附錄A 符號版本帶來的挑戰(zhàn)在3.4節(jié)中我們介紹了符號版本的概念,在4.2節(jié)中我們提到了它的使用讓Elf_Rel.r_info的含義有了新的限制,。在本附錄中我們會列出這幾個限制,,并解釋Leakless如何自動地檢驗并滿足它們。 符號版本的限制當符號版本使用時,,Elf_Rel.info域?qū)⑼瑫r作為動態(tài)符號表的下標以及符號版本表(.gnu.version段)的下標,。符號版本表由Elf_Verneed值構(gòu)成,一個0值或1值有著特殊的含義,,它能夠停止符號版本的過程,,這對攻擊者來說再好不過了,。 為了理解這些限制到底是怎樣的,我們將引入一些定義和命名規(guī)則,。idx為Leakless計算出的Elf_Rel.r_info值所指示的下標,,baseof(x)函數(shù)能夠返回段x的基地址,sizeof(y)返回結(jié)構(gòu)體y的大小,,*是指針解引用操作(即取值),。我們定義了如下變量: sym = baseof(.dynsym) + idx · sizeof(Elf Sym) ver = baseof(.gnu.version) + idx · sizeof(Elf Verneed) verdef = baseof(.gnu.version_r) + *(ver) · sizeof(Elf Verdef) 為了能夠進行攻擊,需要滿足下面的條件: sym指向攻擊者控制的一塊內(nèi)存區(qū)域,,并且 (b) ver指向攻擊者控制的內(nèi)存區(qū)域,那里會被寫上0值,,或 (c) verdef指向攻擊者控制的內(nèi)存區(qū)域,,在那里將會放上一個正確構(gòu)造的Elf_Verdef結(jié)構(gòu)。 如果不能滿足上述條件將會出現(xiàn)訪問一段未被映射的內(nèi)存區(qū)域或者符號解析過程失敗的現(xiàn)象,,并最終讓程序終止,。 Leakless可以在大多數(shù)情況下自動滿足這些限制。一個典型的成功情形就是讓idx值指向一個值為0的版本下標或者在.text段中(它大多跟在.gnu.version段后面),,并指向一個.data和.bss段中符號,。一個值得注意的例外是x86-64架構(gòu)下支持huge pages的ELF二進制文件 [32]。使用huge pages意味著內(nèi)存頁是以2MB的邊界對齊的,,所以包含只讀段(特別是.gun.version和.text)的segment與可寫的segment(含有.bss和.data)離得很遠,。這就讓找到一個合適的idx值變得很難。 huge page的問題huge page的效果如下面例子所示: “` $ readelf –wide -l elf-without-huge-pages Program Headers : Type VirtAddr MemSiz Flg Align … LOAD 0x00400000 0x006468 R E 0x1000 LOAD 0x00407480 0x0005d0 RW 0x1000 … $ readelf –wide -l elf-with-huge-pages Program Headers : Type VirtAddr MemSiz Flg Align … LOAD 0x00400000 0x00610c R E 0x200000 LOAD 0x00606e10 0x0005d0 RW 0x200000 … “` 在第一個例子里可寫的segments與程序開頭的距離是以KB計的,,但如果用了huge pages距離就超過了2MB,,這樣就無法找到一個有效的idx值了。 對于這個問題,,可以使用兩種方法解決,。 第一種選擇是在只讀的segment(一般是.text段)中為Elf_Verneed找到一個0值。令ro_start, ro_end, ro_size分別為只讀segment的開始地址,,結(jié)束地址以及大小,,rw_start, rw_end, rw_size為可寫segment的對應值。那么它們需要滿足以下條件: ro_start ≤ ver < ro_end rw_start ≤ sym < rw_end 這里,,最難滿足的是.dynsym或.gnu.version位于ro_start的開頭的情況,。假設上述兩條都能滿足,我們就有: idx · sizeof(Elf_Verneed) < ro end ? ro start idx · sizeof(Elf_Sym) ≥ rw start ? ro start 也就是: idx · sizeof(Elf_Verneed) < ro size idx · sizeof (Elf_Sym) ≥ 2 MiB 我們知道Elf_Verneed和Elf_Sym在64位ELF中各占2個字節(jié)與24個字節(jié),,我們可以計算滿足這個不等式組條件下ro_size的最小值,。結(jié)果是170.7KB。如果.rodata段小于這個大小,那就必須使用其他方法,。 第二種選擇是將Elf_Verneed放置于可寫的segment,。在這種情況下,攻擊的要求以下面的不等式組來定義: rw_start ≤ ver < rw_end rw_start ≤ sym < rw_end 如果我們同樣來考慮最壞情況并作出之前那樣的假設,,我們得到: idx · sizeof(Elf_Verneed) ≥ rw_start ? ro_start idx · sizeof(Elf_Sym) < rw_start ? ro_start + rw_size 也就是: idx · sizeof(Elf_Verneed) ≥ 2 MiB idx · sizeof(Elf_Sym) < 2 MiB + rw_size 我們現(xiàn)在可以計算出能夠使其成立的可寫segment(rw_size)大小的下界: 22MB,。然而,這個值沒有道理地大,,并讓我們得出結(jié)論: 這種方法在使用huge pages的小型ELF文件中是不適用的 |
|
來自: astrotycoon > 《鏈接加載》