棧緩沖區(qū)溢出概述
緩沖區(qū)溢出是一種歷史悠久的攻擊手段,在1988爆發(fā)的Morris worm就使用緩沖區(qū)溢出作為其中一種攻擊手段.簡單定義下,緩沖區(qū)溢出就是利用編程語言不進行數(shù)組下標(biāo)越界進行檢查的漏洞,向緩沖區(qū)寫入越界的數(shù)據(jù),破壞緩沖區(qū)相鄰內(nèi)存數(shù)據(jù)的一種手段。
具體而言,緩沖區(qū)溢出包含的變種有棧緩沖區(qū)溢出,堆緩沖區(qū)溢出等。
保護措施
為了保護系統(tǒng)不受緩沖區(qū)溢出的攻擊,研究人員提出了一系列的保護措施,包括:
地址空間隨機化(ASLR),在每次編譯鏈接時,將程序地址空間中的函數(shù)庫,代碼段,數(shù)據(jù)段,堆棧的地址隨機化。
棧不可執(zhí)行(NX),將棧的頁描述符的X位設(shè)置為0, 防止CPU執(zhí)行攻擊者向棧中注入的代碼。
金絲雀(canary),在棧底寫入一個魔數(shù),當(dāng)攻擊者實施棧溢出時,魔數(shù)就會被覆蓋,系統(tǒng)就會檢測到該程序已經(jīng)受到了攻擊。
x86_64函數(shù)調(diào)用慣例及其棧幀
為了向棧中注入我們自己的代碼,首先需要了解的是函數(shù)調(diào)用時的棧的變化。需要提前說明的是,x86是小端結(jié)構(gòu),棧的起點位于高地址。寫一段簡單的代碼:
gcc call.c -o callobjdump -d call
編譯之后,讓我們看看可執(zhí)行文件的情況。
圖3-1
下圖是main函數(shù)和maxn函數(shù)的棧幀變化示意圖圖3-2
下圖是簡化后的函數(shù)的棧的結(jié)構(gòu)
shellcode的編寫
shellcode就是我們注入到目標(biāo)棧中的代碼,shellcode的功能是使用execve函數(shù)得到一個shell。
x86-64的系統(tǒng)調(diào)用
從下圖可見,x86_64架構(gòu)取消了傳統(tǒng)的中斷形式的系統(tǒng)調(diào)用,使用syscall指令實現(xiàn)系統(tǒng)調(diào)用。并且存放參數(shù)的寄存器也有所變化。execve的系統(tǒng)調(diào)用號也從0xb變?yōu)榱?x3b
圖4-132bit和64bit系統(tǒng)調(diào)用對比圖 [object Object]2
代碼實例
為了提高得到shell的成功率,我們使用內(nèi)聯(lián)匯編完成,代碼并不難理解,需要說明的是有三點:
標(biāo)號here和call指令的使用保證程序處于循環(huán)之中,不會自動退出
pop %rdi 將execve的參數(shù) "/bin/sh" 的地址傳入rdi
在執(zhí)行syscall之前需要使用xor rax, rax將rax寄存器清空
call指令將下一條指令的地址入棧保存,這樣參數(shù) "/bin/sh" 的地址也就保存在了棧中
運行命令gcc rop.c編譯完成后,得到可執(zhí)行文件a.out運行之后,可以看到shell成功運行圖4-2
截取shellcode
圖4-3
得到的可執(zhí)行文件包含很多段,我們需要截取出需要的部分由上圖可以看到main函數(shù)的起點是0x4004d6, retq指令的地址為0x4004fe,4fe - 4d6 = 28, 所以我們將截取的大小設(shè)置為32個字節(jié)(8的整數(shù)倍)使用xxd工具完成截取操作,xxd -s0x4d6 -l32 -p a.out > shellcode使用-p參數(shù)輸出單純的16進制數(shù)據(jù)圖4-4
victim
先看一段有漏洞的代碼,緩沖區(qū)大小為32個字節(jié)。
接下來我們需要逐步關(guān)閉一些保護機制
1. 關(guān)閉金絲雀機制
gcc -fno-stack-protector -o victim victim.c
2. 關(guān)閉棧不可執(zhí)行機制
execstack -s victim
3. 關(guān)閉地址空間隨機化
setarcharch-R ./victim
運行多次我們會發(fā)現(xiàn)緩沖區(qū)的地址并不會改變
6
向棧中注入數(shù)據(jù)
接下來到了最關(guān)鍵的一步,向目標(biāo)緩沖區(qū)注入數(shù)據(jù)。有兩個問題:
注入數(shù)據(jù)的內(nèi)容
注入數(shù)據(jù)的長度
注入數(shù)據(jù)的內(nèi)容
最終的目的是改變進程的執(zhí)行路徑,讓其執(zhí)行我們構(gòu)造好的shellcode。由圖4-3可得main函數(shù)的最后一條指令是retq指令,該指令的行為是將棧頂所保存的返回地址賦給rip, 恢復(fù)進程的原執(zhí)行路徑。只要我們將返回地址修改為shellcode的地址,當(dāng)main函數(shù)執(zhí)行retq指令后,就可以讓進程執(zhí)行shellcode。
所以數(shù)據(jù)的內(nèi)容應(yīng)該是 shellcode + 填充字節(jié) + shellcode的地址(從低地址到高地址)
圖6-1
注入數(shù)據(jù)的長度
緩沖區(qū)是32字節(jié),保存的棧底指針rbp是8個字節(jié),返回地址是8個字節(jié),也就是32 + 8 + 8.而shellcode 是32個字節(jié),所以為了覆蓋整個棧幀,填充字節(jié)應(yīng)該是8(8個字節(jié)的rbp),最后是8個字節(jié)的shellcode的地址.也就是32 + 8+ 8.
開始獲取shell
由于x86是小端,所以我們需要顛倒緩沖區(qū)地址的次序(使用tac命令)得到緩沖區(qū)地址addr=$(echo | setarcharch-R ./victim)a=printf %16x $addr | tac -rs..圖6-2
cat shellcode ; printf %016d 0 ;
echo $a | xxd -r -p ;
cat | setarcharch-R ./victim注入后的棧結(jié)構(gòu):
運行上述命令后,可以看到成功得到了一個shell。
總結(jié)
其實,由于地址空間隨機化和棧不可執(zhí)行等保護措施,常規(guī)的棧緩沖區(qū)溢出攻擊已經(jīng)失效.后來為了應(yīng)對棧不可執(zhí)行機制,出現(xiàn)了ret2lib 攻擊,在ret2lib中我們不再向棧中注入代碼,而是利用libc中的系統(tǒng)調(diào)用system來獲取shell.隨后又出現(xiàn)了ROP(面向返回編程).
-
緩沖區(qū)
+關(guān)注
關(guān)注
0文章
33瀏覽量
9096 -
編程語言
+關(guān)注
關(guān)注
10文章
1939瀏覽量
34608 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4308瀏覽量
62445
原文標(biāo)題:黃東升: 棧緩沖區(qū)溢出攻擊實例
文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論