棧溢出介紹
零、前言:
在打pwnable.kr的passcode題目的時候,發現了自己存在一些基礎薄弱,需要補充回來,這是棧溢出的筆記。
一、進程內存:
無論什么計算機架構,進程使用的內存按照功能大致分為四部分:
1、代碼區:
存儲着被轉入的執行的二進制代碼,處理器會到這個區域獲取指令並執行。
2、數據區:
用來存儲局部變量
3、堆區:
進程可以在堆區中動態請求一定大小的內存,並在用完之后歸還給堆區。
動態分配和回收是堆區的特點。
4、棧區:
用於動態的存儲函數之間的調用關系。以保證被調用函數在返回時恢復到主函數中繼續執行。
二、棧:棧和系統棧
棧:棧是一種數據結構,是一種先入后出的數據表,按照一定的規則進行添加和刪除數據。
系統棧:內存中的棧,由系統自動維護,實現高級語言中的函數調用
三、溢出:
在達緩沖區的數據向小緩沖區復制的過程中,由於沒有注意小緩沖區的邊界,導致小緩沖區滿了,從而覆蓋了和小緩沖區相鄰內存區域的其他數據而引起的內存問題。
四、X86通用寄存器:
1、
PUSH:壓入
POP:彈出,刪除
MOV:復制
SUB: sub,esp xxx 即預留xxx字節
2、
ESP:棧頂寄存器,其內存中是一個指針,該指針永遠指向系統棧的最上面的一個棧幀的棧頂。
EBP:棧底寄存器,其內存是一個指針,該指針永遠指向系統棧最上面的要給棧幀的底部。
EIP:指令寄存器,其內存中是一個指針,該指針永遠指向下一條等待執行的指令地址。
3、
EAX:累加器,多種加法乘法指令的默認寄存器
EBX:基地址寄存器,在內存尋址是存放基地址
ECX:計數器,重復(REP)前綴指令和LOOP指令的內定計數器
EDX:被用來放整數除法產生的余數
ESI/EDI:源/目標索引寄存器,在很多字符串操作指令中,DS:ESI指向源串,ES:EDI指向目標串。
五、函數調用過程:
1、步驟:
(1)參數入棧:將參數從右向左依次壓入系統棧。
(2)返回地址入棧:將當前代碼區調用的下一條指令地址壓入棧中,供函數返回時繼續執行。
(3)代碼區跳轉:處理器從當前代碼區跳轉到被執行函數入口。
(4)棧幀調整:
1)保存當前棧幀狀態,以被后面恢復本棧幀使用(push ebp)
2)將當前棧幀切換到新的棧幀(mov ebp,esp)
3)給新棧幀分配空間(esp減去所需空間的大小,抬高棧頂)
2、圖例:
1)普通C程序的內存布局:
Text:
包含要執行的程序代碼
Data:
包含程序需要的全局數據、資源等
Stack:
包含函數的輸入參數,返回地址以及保存函數的局部變量等。
Stack是后進先出的結構。隨着函數的調用,它在內存中(從高地址到低地址)向下尋址。
Heap:保存所有動態分配的內存。每當用malloc(動態分配內存)分配獲取內存指針時,這個地址就是從堆中分配的。
2)重點關注三大寄存器:EBP(棧底)、ESP(棧頂)、EIP(指向)
當執行一個函數的時候,相關參數以及局部變量等都會被記錄在ESP、EBP中間的區域。
一旦函數執行完畢,相關棧幀就會從堆棧中彈出,然后從預先保存好的上下文中進行恢復,以便保持堆棧平衡。
CPU必須要知道函數調用完了后的EIP指向(要去哪里執行),這需要堆棧彈出的過程中進行EIP賦指。
3)main函數調用func()函數的程序:
main函數調用func函數,程序運行后,依次將所有的參數壓入棧中,
func函數執行完成后,相應的棧幀依次彈出,此時存儲返回值的地址被加載到EIP寄存器中,以繼續執行main函數中剩余的部分。
目的就是要控制這個返回值,劫持func函數返回到指定的惡意代碼中區。
3、例子A:
(1)代碼:
1 #include <stdio.h>
2 #include <string.h>
3
4 void function2(){ 5 printf("Execution flow changed\n"); 6 } 7 void function1(char *str){ 8 char buffer[5]; 9 strcpy(buffer,str); 10 } 11 void main(int argc,char *argv[]){ 12 function1(argv[1]); 13 printf("%s\n","Executed normally"); 14 }
編譯:
1 root@kali:~/test# gcc -g -fno-stack-protector -z execstack -o test1 test1.c
1)-g:將調試信息記憶符號等編譯到程序中
2)-fno -stack-protestor:關閉堆棧保護機制
3)-z execstack:打開堆棧可執行機制,即關閉堆棧執行保護
運行與崩潰:
(2)審計調試:
PS:strcpy函數沒有對參數進行檢查邊界大小。
gdb調試:
1)使用list顯示源代碼,然后在strcpy和函數返回的地方下斷點
2)反復運行測試,變換參數內容,找到崩潰的地址
EBP和ESP地址
strcpy執行中的EBP和ESP地址
strcpy執行過后的EBP和ESP
3)找到EIP地址
注意:EIP在EBP的下方,即
4)找到function2的開始地址:
(3)利用:
1)利用思路:
2)利用payload:
A: 相對於EBP(找到頭暈)
B:相對於ESP(找到頭暈+1)
C:直接地址相加:
1 (gdb) run $(python -c 'print "A" *17 + "\xb9\x11\x40\x00"')
4、例子B:
(1)代碼:
(2)函數調用過程:
1、壓入寄存器eax,ecx,edx等
2、壓入參數arg2,arg1
3、壓人返回地址eip
4、壓入ebp
5、壓入temp
6、壓入buffer
7、壓入ebx,esi和edi等寄存器
(3)利用:
1)思路:
由於局部變量buffer的長度是4個字節,arg1參數可控,
而strcpy函數並沒有判斷參數的長度,
所以傳入的arg1的長度大於4個字節時,多出來的字節將會依次覆蓋掉局部變量,ebp,返回地址等
通過精心構造arg1的值,就可以將返回地址覆蓋為任意想要的值,挑到shellcode。
2)利用:
5、例子C:修改鄰接變量
函數的局部變量在棧中一個挨着一個,如果這些局部比辦理中有數組之類的緩沖區,
並且程序中存在數組越界的缺陷,那么越界的數組元素就有可能破壞棧中相鄰變量的值,
甚至破壞棧中保存的ebp值、eip值等重要數據。
(1)代碼:
1 #include <stdio.h>
2 #include <string.h>
3 #define PASS_WORD "1234567"
4
5 int verify_password(char * password) 6 { 7 int authentitated; 8 char buffer[8]; 9 authentitated = strcmp(password,PASS_WORD); 10 strcpy(buffer,password); 11 return authentitated; 12 } 13
14 int main() 15 { 16 int valid_flag = 0; 17 char password[1024] = {0}; 18 while (1) 19 { 20 printf("please input password:"); 21 scanf("%s",password); 22 valid_flag = verify_password(password); 23 if(valid_flag) 24 { 25 printf("incorrect password!\r\n"); 26 } 27 else
28 { 29 printf("Congratulation ! you have passed the verification !\r\n"); 30 } 31 } 32 return 0; 33 }
(2)審計:
1)main函數輸入password,調用verify_password函數驗證輸入的password是否等於1234567,相等返回0,否則返回1。
2)strcpy函數存在漏洞和buffer[8],構造利用條件
3)利用緩沖區溢出,修改值,返回0,完成驗證。
思路:在verify_password棧幀中
authenticated位於buffer[8]的下方
authenticated是int型,在內存中占4個字節
buffer[8]占8個字節
控制buffer[]填滿8個字節,然后越界1個字節,緩沖區溢出,使得原authenticated的1覆蓋為0,返回通過。
6、例子C:修改函數返回地址
控制buffer[8]越界,覆蓋其他值,修改eip
六、溢出關鍵函數:
1、輸入:
gets
scanf
vscanf
2、輸出:
sprintf
3、字符串:
strcpy
strcat
bcopy
4、等
七、棧溢出步驟:
1、尋找危險函數:
2、確定填充長度:計算所能操作的地址和所要覆蓋的地址的距離長度
(1)方法:
1)相對於棧底地址
2)相對於棧頂地址
3)直接地址
(2)覆蓋:
1)覆蓋函數返回地址
2)覆蓋變量內容
3)覆蓋bss段某變量內容:bss段(指用來存放程序中未初始化的全局變量的一塊內存區域,bss段屬於靜態內存分配。)
4)等
3、利用:
當完全控制這個程序后,使用一個直接存在指令地址來覆蓋這個返回地址后,CPU將會轉而執行我們的指令。
在uinx/Linux系統中,指令可以執行一個shell,這個shell將會獲得和被溢出程序相同的權限。
如該程序是root權限,那么就會獲得root shell
八、參考鏈接:
https://www.cnblogs.com/Donoy/p/5690402.html
https://www.sohu.com/a/226035403_268160
https://www.jianshu.com/p/58d03dd3680a
https://blog.csdn.net/aemperor/article/details/47310593
https://blog.csdn.net/guiguzi1110/article/details/77663046