關(guān)于Windows上地址空間布局隨機(jī)化防御機(jī)制的分析(上)
地址空間配置隨機(jī)加載(Address space layout randomization,縮寫ASLR,又稱地址空間配置隨機(jī)化、地址空間布局隨機(jī)化)是一種防范內(nèi)存損壞漏洞被利用的計(jì)算機(jī)安全技術(shù)。詳細(xì)一點(diǎn),就是地址空間配置隨機(jī)加載是一種針對(duì)緩沖區(qū)溢出的安全保護(hù)技術(shù),通過對(duì)堆、棧、共享庫映射等線性區(qū)布局的隨機(jī)化,通過增加攻擊者預(yù)測(cè)目的地址的難度,防止攻擊者直接定位攻擊代碼位置,達(dá)到阻止溢出攻擊的目的的一種技術(shù)。
所以對(duì)于攻擊者來說,繞過地址空間布局隨機(jī)化的防御體系是他們執(zhí)行所有內(nèi)存攻擊漏洞的先決條件。這意味著,關(guān)于攻破地址空間布局隨機(jī)化的研究話題也是一個(gè)比較熱門的領(lǐng)域。另外根據(jù)我的推測(cè),對(duì)地址空間布局的攻擊將來也會(huì)變得異常復(fù)雜。
本文會(huì)介紹有關(guān)地址空間布局的一些基本事實(shí),重點(diǎn)是Windows實(shí)現(xiàn)。除了介紹地址空間布局在改善安全狀況方面所做的貢獻(xiàn)之外,我們還旨在為防御者提供有關(guān)如何改善其程序安全性的建議,并為研究人員提供更多有關(guān)地址空間布局的工作原理和調(diào)查其局限性的想法的見解。
當(dāng)程序錯(cuò)誤地將攻擊者控制的數(shù)據(jù)寫入目標(biāo)內(nèi)存區(qū)域或目標(biāo)內(nèi)存范圍之外時(shí),就會(huì)發(fā)生內(nèi)存損壞漏洞(Memory corruption vulnerability)。這可能會(huì)使程序崩潰,更糟糕的是,攻擊者可以完全控制系統(tǒng)。盡管蘋果,谷歌和微軟等大型公司都努力在緩解內(nèi)存破壞漏洞,但數(shù)十年來這個(gè)攻擊一直在困擾著它們。
由于這些漏洞很難被發(fā)現(xiàn),而且一旦發(fā)生就會(huì)危及整個(gè)操作系統(tǒng),所以安全專業(yè)人員設(shè)計(jì)了一種漏洞安全保護(hù)機(jī)制,以阻止程序被利用。另外使用這種漏洞安全保護(hù)機(jī)制,如果發(fā)生內(nèi)存損壞漏洞時(shí),就可以限制所造成的損害。本文將介紹一種被稱為“silver bullet”的方法,防護(hù)人員使用這種方法可以讓漏洞利用變得非常困難,該防護(hù)機(jī)制可以將錯(cuò)誤的代碼留在原處,從而為開發(fā)人員提供了用內(nèi)存安全的語言修復(fù)或重寫代碼所需的時(shí)間。不幸的是,沒有什么防護(hù)方法是完美的,但是地址空間布局隨機(jī)化是可用的最佳緩解措施之一。
地址空間布局隨機(jī)化的工作方式打破了開發(fā)人員在運(yùn)行時(shí)對(duì)程序和庫位于內(nèi)存中的位置所做的假設(shè),一個(gè)常見的示例是面向返回的編程(ROP)中使用的小工具的位置,該位置通常用于抵御數(shù)據(jù)執(zhí)行保護(hù)(DEP)的防御。地址空間布局隨機(jī)化混合了易受攻擊進(jìn)程的地址空間(主程序、其動(dòng)態(tài)庫、堆棧和堆、內(nèi)存映射文件等),因此必須針對(duì)受害進(jìn)程的地址空間專門定制利用有效載荷當(dāng)時(shí)的布局。如果攻擊者編寫一種蠕蟲,將其帶有硬編碼內(nèi)存地址的內(nèi)存破壞漏洞隨機(jī)地發(fā)送到它可以找到的每臺(tái)設(shè)備,這種傳播注定會(huì)失敗。只要目標(biāo)進(jìn)程啟用了地址空間布局隨機(jī)化,漏洞利用的內(nèi)存偏移就將與地址空間布局隨機(jī)化選擇的內(nèi)存偏移不同。這會(huì)使易受攻擊的程序發(fā)生崩潰,而不是被利用。
地址空間布局隨機(jī)化是在Windows Vista中被引入的, 也就是說Vista之前的版本沒有ASLR。更糟糕的是,他們竭盡全力在所有進(jìn)程和設(shè)備上保持一致的地址空間
Windows Vista和Windows Server 2008是最早支持地址空間布局隨機(jī)化兼容可執(zhí)行文件和庫的版本,所以有人可能會(huì)認(rèn)為以前的版本根本沒有地址空間布局隨機(jī)化,而只是將DLL加載到當(dāng)時(shí)方便的任何位置,這個(gè)位置是可以預(yù)測(cè)的,但在兩個(gè)進(jìn)程或設(shè)備之間不一定相同。不幸的是,這些老的Windows版本反而無法實(shí)現(xiàn)我們所說的“地址空間布局一致性”。下表顯示了Windows XP Service Pack 3某些核心DLL的“首選基地址”。

Windows DLL包含一個(gè)首選的基地址,如果沒有地址空間布局隨機(jī)化,則在可能的情況下使用。
創(chuàng)建進(jìn)程時(shí),Vista之前的Windows版本會(huì)盡可能在其首選基址處加載程序所需的每個(gè)DLL。例如,如果攻擊者在ntdll的0x7c90beef處找到了有用的ROP小工具,則攻擊者可以假定它將一直在該地址可用,直到以后的Service Pack或安全補(bǔ)丁要求重新組織DLL為止。這意味著對(duì)Vista之前的Windows的攻擊可以將來自常見DLL的ROP小工具鏈接在一起,以禁用DEP,DEP是這些版本中唯一的內(nèi)存損壞防護(hù)機(jī)制。Data Execution Prevention(DEP),即數(shù)據(jù)執(zhí)行保護(hù),是Windows上的可執(zhí)行空間保護(hù)策略,能夠在內(nèi)存上執(zhí)行額外檢查以阻止數(shù)據(jù)頁(如默認(rèn)的堆頁、各種堆棧頁及內(nèi)存池頁)執(zhí)行惡意代碼,防止緩沖區(qū)溢出攻擊。緩沖區(qū)溢出攻擊的根源在于計(jì)算機(jī)對(duì)數(shù)據(jù)和代碼沒有明確區(qū)分,當(dāng)程序溢出成功轉(zhuǎn)入數(shù)據(jù)頁的shellcode時(shí),會(huì)成功執(zhí)行惡意指令。而DEP的基本原理是將數(shù)據(jù)所在內(nèi)存頁標(biāo)識(shí)為不可執(zhí)行,當(dāng)程序溢出成功轉(zhuǎn)入shellcode嘗試在數(shù)據(jù)頁面上執(zhí)行指令時(shí),CPU 會(huì)拋出異常,而不執(zhí)行惡意指令。微軟從Windows XP SP2開始提供DEP支持,操作系統(tǒng)通過在內(nèi)存的頁面表(Page Table)設(shè)置內(nèi)存頁的NX/XD屬性標(biāo)記,來指明不能從該內(nèi)存執(zhí)行代碼。當(dāng)該標(biāo)識(shí)位設(shè)置為0時(shí)表示這個(gè)頁面允許執(zhí)行指令,設(shè)置為1時(shí)表示該頁面不允許執(zhí)行指令。
為什么Windows需要支持首選基地址?因?yàn)閃indows dll的設(shè)計(jì)與ELF共享庫等其他設(shè)計(jì)之間的性能需要進(jìn)行權(quán)衡。由于Windows DLL不是位于獨(dú)立位置的,特別是在32位計(jì)算機(jī)上,如果Windows DLL代碼需要引用全局變量,則該變量的運(yùn)行時(shí)地址將被硬編碼到計(jì)算機(jī)代碼中。如果DLL加載的地址與預(yù)期的地址不同,則將執(zhí)行重定位以修復(fù)此類硬編碼的引用。如果DLL被加載為它的首選基址,則不需要重新定位,并且DLL的代碼可以直接從文件系統(tǒng)映射到內(nèi)存中。
直接將DLL文件映射到內(nèi)存,這樣做對(duì)性能的提高有好處,因?yàn)樗苊饬嗽谛枰狣LL的任何頁面之前將它們讀入物理內(nèi)存。首選基址的一個(gè)更好的理由是確保內(nèi)存中只需要一個(gè)DLL副本,如果沒有它們,那么運(yùn)行的三個(gè)程序?qū)⒐蚕硪粋€(gè)公共DLL,但是每個(gè)程序在不同的地址加載該DLL,那么內(nèi)存中就會(huì)有三個(gè)DLL副本,每個(gè)副本都被重新定位到不同的庫。這樣一來,使用共享庫的主要好處就會(huì)被抵消掉。除了安全性方面的好處外,地址空間布局隨機(jī)化還以一種更優(yōu)雅的方式完成了同樣的任務(wù),即確保已加載DLL的地址空間不會(huì)重疊,并且只將DLL的一個(gè)副本加載到內(nèi)存中。由于地址空間布局隨機(jī)化在避免地址空間重疊方面比靜態(tài)分配的首選負(fù)載地址做得更好,所以手動(dòng)分配首選基地址在支持地址空間布局隨機(jī)化的操作系統(tǒng)上不提供任何優(yōu)化,并且在開發(fā)生命周期中不可能再被用到。
總結(jié)起來就是:
1. Windows XP和Windows Server 2003及更早版本不支持地址空間布局隨機(jī)化,顯然,這些版本已經(jīng)多年不受支持了,應(yīng)該早就停止在生產(chǎn)環(huán)境中使用了。他們可能沒有意識(shí)到,完全相同的程序可能更安全,也可能更不安全,這取決于運(yùn)行的操作系統(tǒng)版本。那些仍然擁有地址空間布局隨機(jī)化和非地址空間布局隨機(jī)化支持Windows版本的開發(fā)人員應(yīng)該相應(yīng)地響應(yīng)CVE報(bào)告,因?yàn)橥瑯拥穆┒纯赡茉赪indows 10上無法利用,但在Windows XP上卻可以利用。這同樣適用于Windows 10而不是Windows 8.1或Windows 7,因?yàn)槊總€(gè)版本的ASLR都變得更加強(qiáng)大。
2. 審核老版程序代碼庫,以避免被首選加載地址誤導(dǎo)。老版程序仍可以使用Microsoft Visual C ++ 6之類的老工具進(jìn)行維護(hù),這些開發(fā)工具包含有關(guān)首選加載地址的作用和重要性的過時(shí)文檔。由于這些老工具無法將映像標(biāo)記為與地址空間布局隨機(jī)化兼容,因此,一個(gè)懶惰的開發(fā)人員不必費(fèi)心更改默認(rèn)的DLL地址,實(shí)際上會(huì)更好,因?yàn)闆_突會(huì)迫使映像重新定位到無法預(yù)測(cè)的位置!
Windows在跨進(jìn)程甚至跨用戶的同一位置會(huì)加載多個(gè)映像實(shí)例,只有重新啟動(dòng)才能保證所有映像的地址均具有新的隨機(jī)基地址。
在地址空間布局隨機(jī)化的Linux實(shí)現(xiàn)中使用的ELF映像可以在共享庫中使用與位置無關(guān)的可執(zhí)行文件和與位置無關(guān)的代碼,以便在每次啟動(dòng)時(shí)為主程序及其所有庫提供新的隨機(jī)地址空間,共享同一設(shè)備的代碼在多個(gè)進(jìn)程之間可以進(jìn)行切換,即使它被加載到不同的地址也是如此。 不過,Windows ASLR無法以這種方式工作。相反,第一次使用內(nèi)核時(shí),內(nèi)核會(huì)為每個(gè)DLL或EXE映像分配一個(gè)隨機(jī)的加載地址,并且在加載DLL或EXE的其他實(shí)例時(shí),它們會(huì)收到相同的加載地址。如果映像的所有實(shí)例均已卸載,并且該映像隨后又被加載,則該映像可能會(huì),也可能不會(huì)接收到相同的基址。只有重新啟動(dòng),才能保證系統(tǒng)范圍內(nèi)所有映像接收到的都是最新基址。
由于Windows DLL不使用與位置無關(guān)的代碼,因此可以在進(jìn)程之間共享其代碼的唯一方法是始終將其加載在同一地址。為了實(shí)現(xiàn)這一點(diǎn),內(nèi)核會(huì)選擇一個(gè)地址(例如32位系統(tǒng)上的0x78000000),并在其下面的隨機(jī)地址處加載DLL。如果某個(gè)進(jìn)程加載了最近使用的DLL,則系統(tǒng)可能會(huì)重新使用先前選擇的地址,因此會(huì)在內(nèi)存中重新使用該DLL的先前副本,該實(shí)現(xiàn)解決了為每個(gè)DLL提供一個(gè)隨機(jī)地址并確保DLL不會(huì)同時(shí)重疊的問題。
對(duì)于EXE,不必?fù)?dān)心兩個(gè)EXE重疊,因?yàn)樗鼈冇肋h(yuǎn)不會(huì)被加載到同一進(jìn)程中。即使映像大于0x100000字節(jié),將EXE的第一個(gè)實(shí)例加載為0x400000,將第二個(gè)實(shí)例加載為0x500000也沒有問題, Windows只選擇在給定EXE的多個(gè)實(shí)例之間共享代碼。
總結(jié)起來就是:
1. 任何在崩潰后自動(dòng)重啟的Windows程序都特別容易受到暴力攻擊,這時(shí)地址空間布局隨機(jī)化的防護(hù)也會(huì)失敗。
考慮遠(yuǎn)程攻擊者可以按需執(zhí)行的程序,例如CGI程序,或者僅在超級(jí)服務(wù)器需要時(shí)才執(zhí)行的連接處理程序(例如inetd)。另一種可能性是,Windows服務(wù)與看門狗配對(duì),當(dāng)它崩潰時(shí)會(huì)重新啟動(dòng)該服務(wù)。此時(shí),攻擊者可以利用Windows ASLR的工作原理來盡可能加載EXE的基地址。如果程序崩潰,并且該程序的另一個(gè)副本保留在內(nèi)存中,或者該程序迅速重新啟動(dòng),并且在可能的情況下,接收到相同的ASLR基址,則攻擊者可以假定新實(shí)例仍為加載到相同的地址,此時(shí),攻擊者將嘗試使用相同的地址。
2. 如果攻擊者能夠發(fā)現(xiàn)DLL在任何進(jìn)程中的加載位置,那么他就知道DLL在所有進(jìn)程中的加載位置。
假如一個(gè)運(yùn)行網(wǎng)絡(luò)服務(wù)的系統(tǒng)同時(shí)具有兩個(gè)漏洞:一個(gè)在調(diào)試消息中泄漏指針值,但沒有緩沖區(qū)溢出;一個(gè)在緩沖區(qū)溢出但不泄漏指針。如果泄漏的程序揭示了kernel32.dll的基址,并且攻擊者知道該DLL中的一些有用的ROP小工具,則可以使用相同的內(nèi)存偏移量來攻擊包含溢出的程序。因此,看似無關(guān)的易受攻擊程序可以鏈接在一起,以首先克服ASLR,然后啟動(dòng)漏洞利用程序。
3. 要提升特權(quán),可以首先使用低特權(quán)帳戶繞過ASLR。
假設(shè)后臺(tái)服務(wù)公開了僅供本地用戶可訪問的命名管道,并且存在緩沖區(qū)溢出。要確定該程序的主程序和DLL的基地址,攻擊者只需在調(diào)試器中啟動(dòng)另一個(gè)副本。然后,可以使用從調(diào)試器確定的偏移量來開發(fā)有效載荷,以利用高特權(quán)進(jìn)程。發(fā)生這種情況是因?yàn)閃indows在保護(hù)EXE和DLL的隨機(jī)基址時(shí)不會(huì)嘗試將用戶彼此隔離。
本文翻譯自:https://www.fireeye.com/blog/threat-research/2020/03/six-facts-about-address-space-layout-randomization-on-windows.html如若轉(zhuǎn)載,請(qǐng)注明原文地址。