緩沖區溢出保護機制
Linux
canary(棧保護)
棧溢出保護是一種緩沖區溢出攻擊的緩解手段,當函數存在緩沖區溢出攻擊漏洞時,攻擊者可以覆蓋棧上的返回地址來讓shellcode能夠得到執行。
當啟用棧保護后,函數開始執行的時候會先往棧里插入cookie信息,該cookie往往放置在ebp/rbp的正上方,當函數真正返回的時候會驗證cookie信息是否合法,如果不合法就停止程序運行。
攻擊者在覆蓋返回地址的時候也會將cookie信息給覆蓋掉,導致棧保護檢查失敗而阻止shellcode的執行。在Linux中我們將cookie信息稱為canary。
gcc在4.2版本中添加了棧保護的編譯參數:
-fstack-protector:只為局部變量中包含長度不小於8byte的char數組的函數插入保護代碼
-fstack-protector-all:滿足以下三個條件都會插入保護代碼:
1、局部變量的地址作為賦值語句的右值或函數參數
2、局部變量包含數組類型的局部變量,不管數組長度
3、帶register聲明的局部變量
FORTIFY
先看一個例子:
void fun(char *s) {
char buf[0x100];
strcpy(buf, s);
/* Don't allow gcc to optimise away the buf */
asm volatile("" :: "m" (buf));
}
用包含參數-U_FORTIFY_SOURCE編譯:
08048450 <fun>:
push %ebp ;
mov %esp,%ebp
sub $0x118,%esp ; 將0x118存儲到棧上
mov 0x8(%ebp),%eax ; 將目標參數載入eax
mov %eax,0x4(%esp) ; 保存目標參數
lea -0x108(%ebp),%eax ; 數組buf
mov %eax,(%esp) ; 保存
call 8048320 <strcpy@plt>
leave ;
ret
用包含參數-D_FORTIFY_SOURCE=2編譯:
08048470 <fun>:
push %ebp ;
mov %esp,%ebp
sub $0x118,%esp ;
movl $0x100,0x8(%esp) ; 把0x100當作目標參數保存
mov 0x8(%ebp),%eax ;
mov %eax,0x4(%esp) ;
lea -0x108(%ebp),%eax ;
mov %eax,(%esp) ;
call 8048370 <__strcpy_chk@plt>
leave ;
ret
可以看出,gcc生成了一些附加代碼,通過對數組大小的判斷替換strcpy, memcpy, memset等函數名,達到防止緩沖區溢出的作用。
NX
NX即No-eXecute(不可執行)的意思,NX的基本原理是將數據所在內存頁標識為不可執行,當程序溢出成功轉入shellcode時,程序會嘗試在數據頁面上執行指令,此時CPU就會拋出異常。
等同於Windows下的DEP。
gcc編譯器默認開啟NX選項,通過-z execstack可以關閉NX。
PIE
Position-Independent-Executable是Binutils,glibc和gcc的一個功能,能用來創建介於共享庫和通常可執行代碼之間的代碼。
標准的可執行程序需要固定的地址,並且只有被裝載到這個地址才能正確執行,PIE能使程序像共享庫一樣在主存任何位置裝載,這需要將程序編譯成位置無關,並鏈接為ELF共享對象。
引入PIE的原因就是讓程序能裝載在隨機的地址,從而緩解緩沖區溢出攻擊。
gcc的-pie和fpie選項
RELRO
設置符號重定向表格為只讀或在程序啟動時就解析並綁定所有動態符號,從而減少對GOT(Global Offset Table)攻擊。
Partial RELRO: gcc -Wl, -z, relro:
ELF節重排
.got, .dtors,etc. precede the .data and .bss
GOT表仍然可寫
Full RELRO: gcc -Wl, -z, relro, -z, now
支持Partial RELRO的所有功能
GOT表只讀
Linux下查看保護機制
checksec
用來檢查可執行文件屬性,例如PIE, RELRO, PaX, Canaries, ASLR, Fortify Source等等屬性。
gdb peda插件:
pwntools: