- 知識(shí)背景
- 棧溢出及防護(hù)
- 堆溢出及防護(hù)
一、知識(shí)背景
關(guān)于棧溢出的知識(shí)背景
1. 典型操作系統(tǒng)的內(nèi)存布局:

分段的意義:
緩存命中率高、訪問控制
2. 進(jìn)程的內(nèi)存布局:

3. 堆棧的區(qū)別:
地址增長(zhǎng)方向
heap - 堆 地址從小到大
stack - 棧 地址從大到小
支持及作用
棧時(shí)具有硬件直接支持的,棧存儲(chǔ)數(shù)據(jù)和控制流信息。
堆是由操作系統(tǒng)的庫(kù)函數(shù)予以支持的,堆存儲(chǔ)數(shù)據(jù)。
4. ESP與EIP:
esp - 棧指針寄存器
esp指向棧頂(存儲(chǔ)棧頂?shù)牡刂罚?br>
push則esp減
pop則esp增
eip - 指令指針寄存器
eip指向.test中存放的代碼指令,指向的指令為即將被執(zhí)行的指令。
5. 棧的工作原理:
棧的作用:為函數(shù)的調(diào)用和執(zhí)行維護(hù)存儲(chǔ)結(jié)構(gòu)。
函數(shù)調(diào)用時(shí):存儲(chǔ)數(shù)據(jù)
函數(shù)返回時(shí):恢復(fù)數(shù)據(jù)
棧幀:調(diào)用函數(shù)將在棧上創(chuàng)建一個(gè)結(jié)構(gòu)化空間,稱為棧幀。
錨點(diǎn):函數(shù)被調(diào)用時(shí)的棧指針位置??捎糜趯?duì)函數(shù)的局部數(shù)據(jù)尋址、返回到調(diào)用者。
call - return的實(shí)現(xiàn):
1)存儲(chǔ)參數(shù)
push arg2
push arg1
2)存儲(chǔ)函數(shù)f返回后需要執(zhí)行的指令地址RA(push RA)
call f
3)存儲(chǔ)返回位置
把ebp原來存儲(chǔ)的值壓棧,將esp此時(shí)的位置X存入ebp。
push %ebp
mov %esp %ebp
4)執(zhí)行函數(shù)f的指令
X可幫助函數(shù)f的局部變量尋址。
5)返回
將ebp存儲(chǔ)的值(即返回位置)告訴esp,并回復(fù)ebp原來的數(shù)據(jù)。此外,通過此時(shí)棧頂?shù)臄?shù)據(jù)RA,定位到下一條需要執(zhí)行的指令。
mov %ebp %esp
pop %ebp
retn
6. 緩沖區(qū):
copy-on-write:
資源的復(fù)制只有在需要寫入的時(shí)候才進(jìn)行,在此之前,只是以只讀方式共享。
很多時(shí)候,函數(shù)執(zhí)行中需要局部分配較大的線性內(nèi)存空間。

增長(zhǎng)方向
棧增值方向?yàn)閺母叩降?,緩沖區(qū)的增長(zhǎng)從低到高。
要求
緩沖區(qū)應(yīng)該足夠大,寫入數(shù)據(jù)之前應(yīng)該先檢查是否仍有剩余空間可用。
關(guān)于堆溢出的知識(shí)背景
1. 堆的創(chuàng)建和維護(hù):
通過malloc實(shí)現(xiàn)

2. 堆的工作原理:
malloc創(chuàng)建的區(qū)域首先是堆的場(chǎng)地(Arena),進(jìn)程申請(qǐng)堆空間時(shí),malloc從場(chǎng)地中劃分對(duì)應(yīng)大小的區(qū)域供其使用。同一場(chǎng)地內(nèi)可能存在多個(gè)堆段。
注意:
Main Arena沒有heap_info
有多個(gè)heap的Thread Arena只有一個(gè)malloc_state
場(chǎng)地的數(shù)量限制 - 與cpu內(nèi)核數(shù)量有關(guān):
32位系統(tǒng):#_of_Arena = 2 * #_of_cores + 1
64位系統(tǒng):#_of_Arena = 8 * #_of_cores + 1
堆塊的形態(tài):
N表示此塊是否屬于main arean; M表示此塊是否是mmap()創(chuàng)建的; P表示前塊是否正在使用。


顯式鏈表結(jié)構(gòu)bin:
bin是記錄free chunk的鏈表數(shù)據(jù)結(jié)構(gòu),在glibc中可分為fastbinsY和bins。
fastbin主要用于高效的分配和回收比較小的內(nèi)存塊,采用LIFO形式的單鏈表結(jié)構(gòu)。

頂塊top chunk:
不屬于任何bin。
當(dāng)前所有空閑塊(無論哪種bin)全都尺寸不合時(shí),由頂塊應(yīng)急。
頂塊比請(qǐng)求尺寸大——分割供給使用,剩余部分為新頂塊。
頂塊比請(qǐng)求尺寸小——全堆無適合塊,擴(kuò)展堆/分配新堆。
二、棧溢出及防護(hù)
1. 主要原因:忽略了邊界檢查
-> 開發(fā)者認(rèn)為被操作對(duì)象合法
-> 二進(jìn)制代碼底層細(xì)節(jié)復(fù)雜,容易使人忽略問題的存在
-> 一些重要的遺產(chǎn)代碼在最初的設(shè)計(jì)中存在此類缺陷
2. 現(xiàn)有的棧溢出防護(hù)技術(shù)
數(shù)據(jù)執(zhí)行保護(hù)(W⊕X,NX-bit):
機(jī)制:
對(duì)內(nèi)存頁(yè)面增加一個(gè)標(biāo)識(shí)比特,使它要么可改寫,要么可執(zhí)行。
缺陷:
只要不試圖執(zhí)行數(shù)據(jù)區(qū)內(nèi)容,則既不能阻止溢出,又不能發(fā)現(xiàn)溢出。
棧幀上的“金絲雀”:
機(jī)制:
棧上設(shè)置檢查點(diǎn),檢查點(diǎn)數(shù)值異常,則代表?xiàng)R绯霭l(fā)生。
缺陷:
開銷大,對(duì)遺產(chǎn)代碼不起作用。
影子棧:
機(jī)制:
構(gòu)造一個(gè)額外的動(dòng)態(tài)數(shù)據(jù)區(qū)域,稱為影子站。當(dāng)call發(fā)生時(shí),在返回地址入棧的同時(shí),也將其寫入影子堆棧。每當(dāng)return發(fā)生時(shí),在彈出并轉(zhuǎn)向返回地址前,將其與影子堆棧的棧頂比較。
缺陷:
開銷大,無法保護(hù)遺產(chǎn)代碼。
分離的控制/數(shù)據(jù)棧:
機(jī)制:
修改操作系統(tǒng)的內(nèi)存布局,分配兩個(gè)棧空間,分別用于控制參數(shù)和數(shù)據(jù)的存儲(chǔ)。
缺陷:
無法保護(hù)遺產(chǎn)代碼,不適用于完全由匯編語(yǔ)言構(gòu)成的函數(shù)。
三、堆溢出及防護(hù)
1. 與棧溢出的區(qū)別
溢出方向等于堆增長(zhǎng)方向。首先破壞(虛擬地址意義上的)下一個(gè)堆塊的構(gòu)造。
2. linux上的典型堆溢出利用方式
- 攻擊fastbin
- 攻擊unlink
攻擊fastbin:
基于fastbin塊LIFO的特點(diǎn),我們可以先申請(qǐng),然后釋放,再申請(qǐng)就可以得到原來地址的塊。
但是這不能滿足我們的需求,我們需要將堆分配在可控地址。
我們可以通過堆溢出更改已經(jīng)申請(qǐng)塊的fd,使其指向我們可控的地址,并且在可控地址上偽造假的fastbin結(jié)構(gòu)。
釋放,再申請(qǐng)兩次,第2次就可以得到分配在可控地址上的塊。(覆蓋fd)

攻擊unlink:
unlink攻擊技術(shù)就是利用”glibc malloc”的內(nèi)存回收機(jī)制,將second chunk給unlink掉,并且,在unlink的過程中使用shellcode地址覆蓋掉free函數(shù)(或其他函數(shù)也行)的GOT表項(xiàng)。這樣當(dāng)程序后續(xù)調(diào)用free函數(shù)的時(shí)候(如上面代碼[5]),就轉(zhuǎn)而執(zhí)行shellcode了。顯然,核心就是理解glibc malloc的free機(jī)制。
一旦涉及到free內(nèi)存,那么就意味著有新的chunk由allocated狀態(tài)變成了free狀態(tài),此時(shí)glibc malloc就需要進(jìn)行合并操作——向前以及(或)向后合并。這里所謂向前向后的概念如下:將previous free chunk合并到當(dāng)前free chunk,叫做向后合并;將后面的free chunk合并到當(dāng)前free chunk,叫做向前合并。


3. windows堆溢出攻擊的主要形式
- 利用向量化異常處理(VEH)
- 利用系統(tǒng)默認(rèn)異常處理函數(shù)(UEF)
- Heap spray
- Bitmap Flipping攻擊
- Bitmap XOR攻擊
- Heap Cache攻擊
4. 堆溢出防御
堆依靠系統(tǒng)庫(kù)實(shí)現(xiàn)其維護(hù),因此,堆保護(hù) = 系統(tǒng)庫(kù)升級(jí)。
針對(duì)unlink的保護(hù):
Double Free檢測(cè)
next size非法檢測(cè)
雙鏈表沖突檢測(cè)