實驗環境、代碼、及准備
https://www.cnblogs.com/lqerio/p/12870834.html
vul5
Snprintf函數,百度百科:
將可變個參數(...)按照format格式化成字符串,然后將其復制到str中。
(1) 如果格式化后的字符串長度 < size,則將此字符串全部復制到str中,並給其后添加一個字符串結束符('\0');
(2) 如果格式化后的字符串長度 >= size,則只將其中的(size-1)個字符復制到str中,並給其后添加一個字符串結束符('\0'),返回值為欲寫入的字符串長度。
查閱資料可知:
snprintf(buf, sizeof buf, arg);
snprintf()函數,該函數的作用為將第三個參數生成的格式化字符串拷貝到第一個參數中,拷貝的大小由第二個參數進行設置。並且其會根據格式化字符串的形式進行替換:在遇到格式化字符串參數之前,它會先將字符拷貝,當遇到格式化字符參數時,該函數會對指定的格式化字符進行替換。
%n:將%n之前printf已經打印的字符個數賦值給偏移處指針所指向的地址位置,如%100×10$n表示將0x64寫入偏移10處保存的指針所指向的地址(4字節),而%$hn表示寫入的地址空間為2字節,%$hhn表示寫入的地址空間為1字節
格式參考了:
https://blog.csdn.net/u010517901/article/details/46486341
https://blog.csdn.net/qq_36779888/article/details/89684453
故此次溢出的構造應該是構造合適的arg,從而構造合適的參數(%n),從而修改snprintf函數的返回地址,跳轉到payload。
shellcode(構造過程)
原理是運行/bin/sh 來得到shell,構造過程是將具有運行/bin/sh的C代碼轉換成有相同功能的機器碼。注意代碼中用到 0 的地方改成用 xor eax,eax,這樣可以避免復制字符串時遇到/0 中斷。
下面的shellcode長度為45字節(不含/0)
/*
* Aleph One shellcode.
*/
static const char shellcode[] =
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh";
exploit5
Gdb /tmp/vul5
Disas foo
0x80484cb 0x80484e3
先構造一個簡單的payload,用做測試
gdb -e exploit5 -s /tmp/vul5
catch exec
r
break *0x80484cb
c
此時剛剛進入foo,ebp,buf地址分別為
B snprintf
此時disas查看snprintf的匯編
查看此時的,ebp,esp及其內容,可見snprintf沒有改變ebp,而esp(0xbffffb2c)指向的是0x80484e8,是snprintf的返回地址(下一條指令),所以我們只要構造合適的payload,覆蓋0xbffffb2c中的返回地址,使其指向我們的payload即可。
所以payload需要使用合適的參數修改esp(0xbffffb2c)的值。能修改的參數只有%n,寫入的數據是已經輸出的字符數,所以只需要構造合適的數目的字符就可以修改為指定地址。(比如40個字符,就寫入0x28)
Vul5中,Snprintf用到%u參數時不斷的修改buf,所以返回的地址不能是buf,那么只能是arg。
查看arg的地址:重新gdb調試,在foo break查看
為 0xbffffcd4,而buf地址在0xbffffb3c,相差408字節,為了不覆蓋到arg,payload需要不超過408字節。(這里我覺得其實snprintf限制了不超過buf的400字節了)
考慮到添加一些nop,最終返回地址可以是&arg+0x40=0xbffffd14
字符串長度不應該過長,所以每次修改esp指向的一字節。比如往esp(0xbffffb2c)寫入0xbffffcf4的f4,需要有16*f+4=244個字符
寫fc需要252個字符,ff需要255字符,最后的bf,需要191個字符
修改地址的payload部分為
然后填nop和shellcode即可
然后這樣出了問題,總是跳到一個隨機的地址
Gdb調試,發現是arg的地址出錯。是0xbffffe62
目標地址改為 0xbffffea2即可