概說
現(xiàn)代的編譯器和系統(tǒng)已經(jīng)實現(xiàn)了很多機制,避免受到緩沖區(qū)溢出的攻擊,下面介紹Linux上比較新版本的GCC所提供的機制。
1. 棧的隨機化
我們在前面文章里提到的一個參數(shù)設置:
sysctl -w kernel.randomize_va_space=0
kernel.randomize_va_space 就是棧隨機化的設置,當它的值為0時,禁止實現(xiàn)棧隨機化,這樣的情況下,程序在同一計算機系統(tǒng)內每次啟動的?;刂范际枪潭ú蛔兊摹?/p>
當它的值為1時,將實現(xiàn)棧隨機化,棧的位置在程序每次運行時都有變化。 即使許多機器都運行同樣的代碼,它們的棧地址都是不同的。
棧隨機化的實現(xiàn)方式是:
程序開始時,在棧上分配一段0~n字節(jié)之間的隨機大小空間,程序不使用這段空間,但它會導致程序每次執(zhí)行時的棧的位置發(fā)生變化。
分配的范圍n必須足夠大,才能獲得足夠多樣的棧地址變化;同時又必須足夠小,不至于浪費程序太多的空間。
在Linux中,棧隨機化是標準行為,它是更大的一類技術中的一種,這類技術稱為地址空間布局隨機化,采用這類技術,程序的不同部分(代碼段、數(shù)據(jù)段、堆棧)都會被加載到存儲器的不同部分。
- 頑固且有耐性的攻擊者可以通過枚舉的方法來蠻力克服隨機化,它反復用不同的地址進行攻擊,來猜測棧的地址。如果它建立一個256字節(jié)的nop sled(空操作),枚舉215=32768個起始地址就能破解223的隨機化。
- 對于64位系統(tǒng),需要嘗試2^24=16777216就有點令人生畏了。
2. 棧破壞檢測
前面的文章里,我們用gcc編譯時都加上了下面的參數(shù)。
"-fno-stack-protector" 這個參數(shù)用來阻止程序生成棧破壞檢測的代碼。
這是因為在新版的GCC里加入了一種棧保護者機制,用來檢測緩沖區(qū)越界,如果我們不禁止這個功能,那么演示緩沖區(qū)溢出攻擊實例就無法成功。
那么,GCC是怎樣實現(xiàn)這個保護機制的呢?
實現(xiàn)的方法是: 在棧幀中任何局部緩沖區(qū)和棧狀態(tài)之間存儲一個只有程序本身才知道的隨機值,俗稱為哨兵,在恢復存儲器狀態(tài)和函數(shù)返回之前,程序檢測哨兵值是否被覆蓋,如果是,那么程序就異常中止。
3.限制可執(zhí)行代碼區(qū)域
我們在前面的文章中編譯程序也用到了下面的一個參數(shù)
execstack
使用這個參數(shù)的目的就是將限制可執(zhí)行代碼區(qū)域的限制取消,使我們的演示能順利進行。
這種方法的實現(xiàn)是和虛擬存儲器的頁表條目有關的,在每個頁表條目里有三個權限位用來控制對頁的訪問,其中XD就是禁止CPU在這個頁表所對應的空間里讀取指令,亦即是在這個區(qū)域里限制可執(zhí)行代碼。
1.現(xiàn)代的處理器都使用虛擬尋址的尋址形式,CPU通過一個生成的虛擬地址來訪問內存,這些虛擬地址就是通頁表條目來記錄管理的。
2.頁表條目是由幾個權限位(有效位)和一個n位地址字段組成。
4. 總結
我們講到的這些技術——隨機化、棧保護和限制可執(zhí)行代碼,是用于最小化程序緩沖區(qū)溢出攻擊漏洞三種最常見機制,它們都有同樣的屬性,就是不需要程序員任何特殊的努力,帶來的性能代價都非常小,甚至沒有。這三種機制都很有效,三種結合起來大大提高了程序的安全性,不幸的是,仍然有辦法能夠攻擊到計算機。
什么辦法呢?
后面的章節(jié)見解!