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

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