CMD.EXE中dir超長字串緩衝區溢位原理學習

Andrew.Hann發表於2013-07-31

最近看逍遙的《網路滲透攻擊與安防修煉》講到CMD命令視窗的dir傳超長字串溢位的例子。自己實驗了一下,的確會產生程式崩潰,但是具體什麼原理沒太詳細說,這裡做一下原理探究,權當學習筆記了。

1. 實驗環境 XP SP3

我發現在XP下的cmd.exe有這個漏洞,而在win7下之後的cmd.exe就沒有這個漏洞了

我的理解是這個cmd.exe程式本來就是windows的一個80x86的真實模式模擬環境,在win下又進行了重寫,加固了程式。所以我接下來的實驗材料取自XP下的cmd.exe

 

2. 漏洞定位

我們都知道,不管是ODAY還是利用錯誤提示挖掘軟體漏洞,第一步都要先定位出漏洞的位置,即呼叫哪個函式出錯了,或者對某些非法記憶體進行讀寫出錯了。只有搞清楚了發生錯誤的位置,我們才能進一步利用漏洞。好,我們用OD一步步的往下除錯。

載入CMD.EXE,按F9繼續執行,因為我們知道cmd一定會在某處停下來等待使用者互動,然後才執行下一步操作。也就是會停在windows訊息函式的那個無限迴圈的位置。所以我們大膽的按F9。

這個時候程式UI出現瞭如下的字樣:

Microsoft Windows XP [版本 6.1.7601]
(C) 版權所有 1985-2001 Microsoft Corp.

C:\Users\Administrator\Desktop>

即開始進入無限的訊息迴圈,等待使用者的進一步輸入。這時候我們按下F12暫停程式,並開啟函式呼叫棧 ALT + K。我們通過這種方法來追蹤cmd程式會停在什麼地方以及都呼叫了什麼函式。

注意到呼叫堆疊中的這一行。    

主執行緒, 條目 1
 地址=0018FDC0
 堆疊=4AD0EFEF
 函式過程 / 引數=kernel32.ReadConsoleW
 呼叫來自=cmd.4AD0EFE9
 結構=0018FDBC

很明顯,這是一個win api,用來獲取使用者的輸入引數的。我們順藤摸瓜,對這個函式下斷。重新執行一次。斷點後按F8,程式開始等待我們輸入,注意:這裡開始就是重點了。我們為了能更容易的看出問題。我們輸入多一點的字串 dir \\?\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa......aaaaaaaaaaaa(輸300個左右就可以了),按下回車,程式繼續恢復執行。

 

接下來就是漫長的追蹤過程,不斷按F8,如果遇到了某個call執行後出錯,即跳進了NTDLL的raiseexception領空,按“-”減號退回一條指令,即上一條指令,說明錯誤在這個call裡面,對這個call下斷,重新執行一次,F9執行到這裡後,F7進入..............

重複這個過程,直到來到關鍵的程式碼處:

即執行了一個win api程式竟然也會發生錯誤,進入了NTDLL的SEH錯誤處理領空。只是一個簡單的wcscpy複製函式怎麼會觸發錯誤呢?這種情況按照我之前的經驗來說只發生過一次,就是複製導致了棧空間的溢位(不是說緩衝區溢位,是整個棧溢位,就是複製太多太多字串了,整個棧都被填滿了,棧越界了,導致CPU報錯)。那這次是不是這個情況呢?我帶著這個疑問繼續探究。

 

首先,通過MSDN,我們搜尋一下這個wcscpy的函式宣告:

wchar_t* wcscpy (wchar_t* destination, const wchar_t* source);
Copy wide stringCopies the C wide string pointed by source into the array pointed by destination, including the terminating null character (and stopping at that point).

To avoid overflows, the size of the array pointed by destination shall be long enough to contain the same C wide string as source (including the terminating null character), and should not overlap in memory with source.

這個函式是用來從source指定的位置開始把UNICODE字串逐個複製到destination。這個UNICODE字串以0000作為結束。

好,我們再來觀察一下暫存器和堆疊中關於source和destinatino的情況。

0x18E6B4是源地址,到資料視窗跟蹤一下看看:

沒啥,很正常的UNICODE資料。
我們繼續檢視dest的情況,destination的地址是0x18E8C0。定位到資料區看看。

可以看到,dest的位置落在了我們輸入的字串的範圍內(也就是落在了source + sizeof(輸入資料)的範圍內)。
看到這裡,我突然就明白了。因為wcscpy是逐個字元複製的,但是因為我們輸入的引數超長了,超過了給source分配的空間,於是輸入資料就覆蓋了一部分dest的空間,導致dest的起始位置落在了soure的範圍內
。這樣,就導致每次都source複製一個,就給dest複製一個,兩邊同步增長,就像兩輛速度一樣的騎車並行,而直接的後果就是wcscpy一直看不到0000這個結束符,就會一直複製下去。最終的結果就是超過
執行緒棧的最大空間1M。CPU報錯棧溢位。觸發SEH。

我們把dest - source - 3 * 2 = 256。也就是說我們填入256個a是正好不觸發錯誤的極限。
dir \\?\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

填入257個及以上a就會發生覆蓋。導致錯誤。

為了驗證這個觀點,我們可以填入256個a,然後在wcscpy複製函式的api執行處斷下:

可以看到,指標正好處在source和dest的邊界位置。
如果繼續增長引數長度,就會導致wcscpy的無限迴圈複製,最終導致執行緒棧的溢位。
至此,cmd的dir溢位原理基本搞清楚了,我自己感覺這只是導致了溢位錯誤,至於要怎麼利用這個漏洞進行shellcode執行,一時還沒想到。如果有朋友知道的話,望不吝賜教。我非常希望能有一樣喜歡信安的
朋友一起討論問題。
qq:306211321

相關文章