棧溢位——返回地址
阿新 • • 發佈:2021-08-16
淹沒返回地址
處的引數壓棧開始,如果把返回地址覆蓋成該地址,那麼
覆蓋鄰接變數的方法利用條件太過苛刻,需要原始碼的結構符合漏洞利用才能實行。直接修改EBP或者函式返回地址的攻擊則更為通用。
0x00 原始碼
由於鍵盤能夠直接輸入的字元ASCII範圍有限,無法表達0x11
、0x12
等值,所以對程式碼稍作修改,通過讀取文字檔案輸入。
#include <stdio.h> #define PASSWORD "1234567" int verify_password (char *password) { int authenticated; char buffer[8]; authenticated=strcmp(password,PASSWORD); strcpy(buffer,password); // Overflow return authenticated; } main() { int valid_flag=0; char password[1024]; FILE * fp; if(!(fp=fopen("./password.txt","rw+"))) { exit(0); } fscanf(fp,"%s",password); valid_flag = verify_password(password); if(valid_flag) { printf("Incorrect password!\n"); } else { printf("Congratulation! You have passed the verification!\n"); } fclose(fp); }
使用Visual C++ 6.0編譯,編譯選項預設,Build版本為Debug。務必避開Visual Studio系列的GS編譯選項。
0x01 分析
溢位工作包括:
- 瞭解棧中的情況,如函式地址距離緩衝區的偏移量等。雖然可以通過分析程式碼得到,但最好還是通過動態除錯挖掘;
- 得到程式通過驗證的指令地址,以便使程式直接跳轉到這個分支;
- 在password.txt中的相應偏移處填寫該地址。
首先反彙編得出驗證通過分支的指令地址為0x00401122
,函式verify_password
在0x00401102
處被呼叫,在0x0040110A
處將EAX中函式返回的值取出,在0x0040110D
處與0比較,再決定跳轉到哪個分支。
驗證通過的分支從0x00401122
0x00401102
處的函式呼叫返回後,程式無論如何將跳轉到驗證通過的分支。
0x02 溢位
以4個位元組為單位進行輸入,為方便檢視,每個單位都為“abcd”。
緩衝區buffer
容量為8個位元組,需要2個單位填充。按照棧幀結構,向下為變數authenticated
,需要1個單位填充。再向下為前棧幀EBP,需要1個單位填充。再向下為函式返回地址,需要1個單位填充。
通過十六進位制編輯方式製作password.txt:
注意:動態除錯時顯示的地址是經過轉換而便於閱讀的,其實際儲存方式為小端儲存。
之後通過OllyDbg除錯執行該程式,最終的棧情況為:
區域性變數名 | 記憶體地址 | 偏移3處的值 | 偏移2處的值 | 偏移1處的值 | 偏移0處的值 |
---|---|---|---|---|---|
buffer[0~3] | 0x0012FB14 |
0x61 ('a') |
0x62 ('b') |
0x63 ('c') |
0x64 ('d') |
buffer[4~7] | 0x0012FB18 |
0x61 ('a') |
0x62 ('b') |
0x63 ('c') |
0x64 ('d') |
authenticated(修改前) | 0x0012FB1C |
0x00 |
0x00 |
0x00 |
0x01 |
authenticated(修改後) | 0x0012FB1C |
0x61 ('a') |
0x62 ('b') |
0x63 ('c') |
0x64 ('d') |
前棧幀EBP(修改前) | 0x0012FB20 |
0x00 |
0x12 |
0xFF |
0x80 |
前棧幀EBP(修改後) | 0x0012FB20 |
0x61 ('a') |
0x62 ('b') |
0x63 ('c') |
0x64 ('d') |
返回地址(被覆蓋前) | 0x0012FB24 |
0x00 |
0x40 |
0x11 |
0x07 |
返回地址(被覆蓋後) | 0x0012FB24 |
0x00 |
0x40 |
0x11 |
0x22 |
執行情況:
由於EBP傳入了無效值,程式崩潰,但成功到達驗證通過的分支。