最近看逍遙的《網絡滲透攻擊與安防修煉》講到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