淹沒傳回位址
覆寫鄰接變量的方法利用條件太過苛刻,需要源代碼的結構符合漏洞利用才能實行。直接修改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] | | ('a') | ('b') | ('c') | ('d') |
buffer[4~7] | | | | | |
authenticated(修改前) | | | | | |
authenticated(修改後) | | | | | |
前棧幀EBP(修改前) | | | | | |
前棧幀EBP(修改後) | | | | | |
傳回位址(被覆寫前) | | | | | |
傳回位址(被覆寫後) | | | | | |