其實安裝這個系統的本意是折騰老游戲,裝點菠蘿1之類的玩玩,結果找到了個漏洞。

這個漏洞位于cmd.exe,是一個緩沖區溢出漏洞,我在公開資料上沒有查詢到該漏洞的任何信息,也不知道是我沒查到,還是確實沒人公開它,如果有誰查到這個漏洞的信息了記得提醒我一下。

實驗環境

Microsoft Windows 2000 5.00.2195 Service Pack 4

用到的軟件工具

010Editor

ollydbg

IDA pro

一、漏洞簡述

cmd.exe是windows系統傳統的命令行小工具,從最早的windows系統一直到現在的windows 11都有這個小工具,當利用這個小工具對FAT32格式的磁盤目錄進行dir操作時,若該目錄里的文件的文件名長度剛好為260個字符時,cmd.exe進程崩潰閃退。

二、cmd.exe的漏洞發現過程

文件構造

本漏洞需要在磁盤上構造一個文件名長度剛好260個字符的特殊文件,但是windows系統在資源管理器或者命令行工具里不允許文件名全路徑長度超過260個字符,所以需要借助二進制編輯軟件對磁盤分區卷進行直接的編輯長文件名目錄項,構造這種特殊的文件。這里我用了010Editor。

我在U盤H分區根目錄建立一個共有20個長文件名目錄項的文件,由于一個目錄項32字節里有26個字節可以存儲13個寬字符的文件名,這個文件的長文件名為aaaaaaaa…..aaaagf,一共有260個寬字符。

漏洞跟蹤

將構造好的U盤插入Windows 2000系統,在ollydbg中開啟cmd.exe進程,如下圖:

在命令行窗口執行dir e:,進程出現異常。這里我用了污點數據追蹤的方法來找程序的crash point,也就是說在堆棧區找到我們構造的污點數據aaaaaaaa…..aaaagf,在這個數據附近尋找函數一開始壓入的返回地址,從而追蹤到溢出點,整個過程如下圖:

順便一提,在不同的機器上程序的crash point可能不同,比如說在家里的另一臺電腦上進行實驗,crash時的Call stack是長這樣的:
直接就可以定位到4AD0E060的位置,都不用追蹤污點數據了。

在4AD17B34處調用的wcscpy,將字符串拷貝至棧空間ebp-20c,該空間大小0x20c可以放置262個寬字符,似乎不能造成棧溢出,但事實上在4AD17B34處設置斷點,再次進行dir e:,如下圖:

執行完wcscpy后,函數的返回地址被覆蓋,如下圖(執行前、執行后棧12EA70處的值):

堆棧緩沖區的溢出,覆蓋了函數在棧12EA70處的函數返回地址和后續的棧結構,繼續進程執行由于棧的破環造成對非法地址的訪問而異常終止。

三、緩沖區溢出漏洞的成因

現在的問題是,我構造的長文件名只有260個寬字符,占有0x208個字節的空間,wcscpy拷貝函數的目的地址棧空間明明有0x20c個字節,為什么有足夠的棧空間也造成了溢出?

進一步的追蹤源字符串的來源,發現cmd.exe程序調用了系統內核模塊kernel32.dll的導出函數FindNextFileW對目錄中的文件進行遍歷,依次得到目錄中的長文件名,如下圖:

Windows2000的FindNextFileW函數在對剛好為260個寬字符的長文件名進行的遍歷時,獲得數據存儲在類型為WIN32_FIND_DATA的結構中,該結構體的定義如下:

typedef struct _WIN32_FIND_DATA {    DWORD dwFileAttributes; //文件屬性    FILETIME ftCreationTime; // 文件創建時間    FILETIME ftLastAccessTime; // 文件最后一次訪問時間    FILETIME ftLastWriteTime; // 文件最后一次修改時間    DWORD nFileSizeHigh; // 文件長度高32位    DWORD nFileSizeLow; // 文件長度低32位    DWORD dwReserved0; // 系統保留    DWORD dwReserved1; // 系統保留    TCHAR cFileName[ MAX_PATH ]; // 長文件名    TCHAR cAlternateFileName[ 14 ]; // 8.3格式文件名} WIN32_FIND_DATA, *PWIN32_FIND_DATA;

在windows系統中常常把MAX_PATH宏定義為260,cmd.exe通過FindNextFileW函數遍歷得到長文件名和短文件名在內存空間中相鄰存儲,當磁盤上的長文件名數據剛好260個寬字符時,造成了兩個字符串的合并,如下圖:

進一步研究發現wcscpy的第二個參數指向的源字符串為從磁盤上得到的長文件名,其后緊接著的為該文件的短文件名,正常情況下的長文件名后按照字符串的結尾規則以‘\0’字符結尾表示字符串結束,但當長文件名長度為260個寬字符時,長文件名最后沒有結束字符‘\0’,長文件名和短文件名變成了同一個字符串,短文件名占據了12個寬字符,這樣源字符串的長度為272個寬字符,占據了0x220的堆棧空間,從而造成了返回地址及棧結構的覆蓋。

四、Windows XP SP3系統以及后續的Windows系統中是否存在該漏洞

進一步的,我對Windows XP SP3的cmd.exe小工具進行了逆向分析,發現與windows 2000的代碼極為相似,不同的是Windows XP SP3啟用了GS保護,大多數函數中都進行了堆棧cookie的校驗,但是GS保護只能增加漏洞利用的難度,并不能杜絕緩沖區溢出對進程堆棧結構造成的破壞,若程序代碼相同應該存在同樣的漏洞。如下圖:

在XP系統中,目的字符串的空間為0x210仍然小于長文件名+短文件名后的最大長度0x220,仍然具備潛在的被溢出覆蓋的可能性。

然而事實上,在XP系統下對構造后的U盤進行dir 操作時,并沒有造成進程閃退,而是將文件名的最后一個字符舍棄后顯示文件名,如下圖:

這說明在XP系統下該漏洞并不存在,為了進一步澄清兩個系統的不同,我對XP系統的kernel32.dll的FindNexeFile函數進行了逆向,如下圖:

Windows XP的Kernel32.dll的FindNextFileW函數對文件名的長度進行了檢測,若長度大于0x206的文件名,取值0x206,這樣該函數遍歷得到的文件名最長259個寬字符,仍以字符串結束符’\0’放置在最后一個字節。由此可以合理推測,高版本的windows系統已經修補了該漏洞。

五、shellcode

Windows 2000系統沒有任何GS,ALSR,DEP等后續Microsoft公司在安全方面的提升,所以可以很方便地利用該漏洞編寫shellcode執行任意的程序,這里寫一個啟動計算器的,以下是構造的文件與對應的代碼:

上傳格式不支持,最后的效果請點擊文末閱讀原文查看。