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

ARMv8的函數調用棧是什么意思?調用棧的內存管理是怎樣的
Xilinx SDAccel開發(fā)環(huán)境在X86_64位工作站的運行情況
友善之臂T4成為計算性能與X86_64不分伯仲的ARM
系統(tǒng)調用:用戶棧與內核棧的切換(上)

評論