標籤:

MS08-067漏洞原理分析及還原

0x00 背景

最近利用MS17-010漏洞肆虐的WanaCry勒索病毒讓445埠走進了大眾的視線,除了這次的MS17-010,在08年年底爆出的MS08-067特大漏洞同樣是利用了445埠。

在《Metasploit滲透測試魔鬼訓練營》中講過對MS08-067漏洞原理的分析,不過作者的文筆十分晦澀難懂,讀起來十分難消化,我反覆閱讀鑽研了幾遍,並配合實踐分析,對該部分的內容大致理解了一些,在這裡就用通俗易懂的語言重新組織一遍,就當做分享和學習總結吧。

445埠

首先介紹一下這個引發了諸多特大漏洞的445埠。

TCP 445埠主要運行兩種服務:

  • SMB 網路服務

  • MSRPC 網路服務

SMB(Server Message Block,伺服器消息塊)首先提供了 Windows 網路中最常用的遠程文件與印表機共享網路服務,其次,SMB的命名管道是 MSRPC 協議認證和調用本地服務的承載傳輸層。

SMB 作為應用層協議,其直接運行在TCP 445埠上,也可通過調用 NBT 的 TCP 139埠來接收數據。

MSRPC(Microsoft Remote Procedure Call,微軟遠程過程調用)是對 DCE/RPC 在 Windows 系統下的重新改進和實現,用以支持Windows系統中的應用程序能夠無縫地通過網路調用遠程主機上服務進程中的過程。

DCE/RPC 獨立運行於網路傳輸層協議上,採用的網路傳輸層協議包括:

  • ncacn_ip_tcp => TCP 139

  • ncadg_ip_udp => UDP 135

  • ncacn_np => TCP 139、445

其中,主要使用的是 ncacn_np(SMB命名管道傳輸協議),也就是利用 SMB 命名管道機製作為 RPC 的承載傳輸協議(MSRPC over SMB)。

只有少數如MS09-050是直接針對SMB服務的,而MSRPC作為調用大量本地服務進程的網路介面,常常被利用 MSRPC over SMB 為通道如MS08-067來攻擊本地服務中存在的安全漏洞。

0x01 MS08-067漏洞原理

MS08-067漏洞是通過 MSRPC over SMB 通道調用 Server 服務程序中的 NetPathCanonicalize 函數時觸發的,而 NetPathCanonicalize 函數在遠程訪問其他主機時,會調用 NetpwPathCanonicalize 函數,對遠程訪問的路徑進行規範化,而在 NetpwPathCanonicalize 函數中存在的邏輯錯誤,造成棧緩衝區可被溢出,而獲得遠程代碼執行(Remote Code Execution)。

所謂路徑規範化,就是將路徑字元串中的【/】轉換為【】,同時去除相對路徑【.】和【..】。如:

**/*/./** => ********..** => ****

在路徑規範化的操作中,服務程序對路徑字元串的地址空間檢查存在邏輯漏洞。攻擊者通過精心設計輸入路徑,可以在函數去除【..】字元串時,把路徑字元串中內容複製到路徑串之前的地址空間中(低地址),達到覆蓋函數返回地址,執行任意代碼的目的。

路徑處理流程

NetpwPathCanonicalize 函數並沒有直接進行輸入路徑和規範化,而是繼續調用了下級函數CanonicalizePathName 來進行路徑整理,將待整理的路徑字元串進行規範化,然後再保存到預先分配的輸出路徑緩衝區buffer中

路徑處理流程:

  1. 檢查待整理路徑的第一個字元

  2. 調用msvcrt.dll模塊的wcslen函數計算路徑長度

  3. 調用msvcrt.dll模塊的wcscat函數把待整理路徑全部複製到新申請的內存中

4. 調用wcscpy函數,去掉待整理路徑中第一個表示父目錄的相對路徑複製到strTemp,如:

******....*** => ..***

5.循環調用wcscpy,直到路徑整理完畢

在這裡我們知道了,在規範化複製時要尋找表示父目錄的【..】字元串及其前面的一個【】字元串,將這一段去掉並將新路徑複製。

如圖,第一次檢查時去掉了第一個相對路徑並複製到緩衝區

但是,當【..】字元串在路徑字元串的最前面時,那麼其前面的一個【】就在緩衝區外面了,就是在這裡產生了向前(低地址)的溢出。

緩衝區溢出

需要明確的是,微軟對路徑規範化時的字元串複製可能出現的緩衝區溢出做了初步的防禦。

在每次向緩衝區中複製字元串時,無論是用 wcsccpy 還是 wcscat,在複製前總要比較源字元串的長度,保證長度小於某個值(207),否則不會繼續複製,這一策略確保緩衝區不會向高地址溢出,即當前函數返回時不會發生問題。

但是注意,在規範化表示路徑,尋找父目錄的【..】字元串前面的【】字元時,程序做了判斷和邊界檢查:如果當前比較字元的地址與源字元串地址相同,就表明整個字元串已經查找完畢,程序就會停止查找。

然而它唯獨漏了一種情況,就是當父目錄相對路徑【..】字元串在源字元串的開頭時,在開始查找時比較的字元串(【】到【..】)位於緩衝區之外,這導致了複製的字元串向低地址的溢出,造成函數wcscpy的返回地址被覆蓋。

0x02 漏洞還原分析

實驗環境

  • 靶機 Windows2003 SP0 EN

  • 漏洞組件 netapi32.dll

  • 工具 IDA Pro、OllyDbg

選擇 Windows XP SP3 EN 系統主機作為分析環境,定位到包含該安全漏洞的系統模塊netapi32.dll(路徑C:Windowssystem32)和調用漏洞服務 Server 的進程 svchost.exe,目標進程命令行為

C:WindowsSystem32svchost.exe-k netsvcs

用 IDA pro 打開 netapi32.dll,找到漏洞所在的 NetpwPathCanonicalize 函(每次運行堆棧中的地址會不同,但各函數的地址一樣),如圖在書中提到

查看該函數流程圖,可以看到,此函數並沒有直接進行輸入路徑的規範化, 而是繼續調用了下級函數 CanonicalizePathName

然而在實際操作中並沒有發現 CanonicalizePathName 這個函數,並且多種資料表明應當是調用 CanonPathName 函數進行規範化。

IDA分析 NetpwPathCanonicalize 函數代碼(F5 + 整理 + 主要代碼):

該函數聲明如下:

DWORD NetpwPathCanonicalize( LPWSTR PathName, //需要標準化的路徑 LPWSTR Outbuf, //存儲標準化後的路徑的Buffer DWORD OutbufLen, //Buffer長度 LPWSTR Prefix, //可選參數,當PathName是相對路徑時有用 LPDWORD PathType, //存儲路徑類型 DWORD Flags // 保留,為0 )

w代碼如下:

int __stdcall NetpwPathCanonicalize(LPWSTR PathName, LPWSTR Outbuf, DWORD OutbufLen, LPWSTR Prefix, LPDWORD PathType, DWORD Flags){ bool v7; int result; v7 = !Prefix || !*Prefix; Prefix = (LPWSTR)*PathType; if ( *PathType || (result = NetpwPathType(PathName, (int)&Prefix, 0), !result) ) { if ( v7 || (result = NetpwPathType(Prefix, (int)&Flags, 0), !result) ) { if ( OutbufLen != 0 ) { *Outbuf = 0; result = CanonPathName(Prefix, PathName, Outbuf, OutbufLen, 0); //===================》核心函數,主要處理在這裡,問題也出在這裡《================ if ( !result ) result = NetpwPathType(Outbuf, (int)PathType, 0); } else { result = 2123; } } } return result;}int __stdcall CanonPathName(LPWSTR PathPrefix, LPWSTR PathName, LPWSTR Buffer, DWORD BufferSize, LPDWORD RetSize){ size_t preLen; size_t pathLen; wchar_t pathBuffer[MAX_PATH*2 + 1]; if ( PathPrefix && *PathPrefix ) { preLen = wcslen(PathPrefix); if ( preLen != 0) { if ( preLen > 520 ) //520 = sizeof(pathBuffer) - 1 return 0x7Bu; // ERROR_INVALID_NAME wcscpy(pathBuffer, PathPrefix); if ( pathBuffer[preLen-1] != \ && pathBuffer[preLen-1] != /) //===================判斷前綴是否以或/結尾======================== { wcscat(pathBuffer, L""); ++preLen; } if ( PathName[0] == \ || PathName[0] == / ) ++pathLen; } } else { pathBuffer[0] = 0; } pathLen = wcslen(PathName); if (pathLen + preLen > sizeof(pathBuffer) - 1) return 0x7Bu; // ERROR_INVALID_NAME wcscat(pathBuffer, PathName); if ( pathBuffer ) { do //===================該循環把路徑中的/轉換成====================== { if ( *pathBuffer == / ) *pathBuffer = \; ++pathBuffer; } while ( *pathBuffer ); } if ( !sub_71C4A2CA() && !ConPathMacros(pathBuffer) ) //====================》ConPathMacros中存在緩衝區溢出《======================= return 0x7Bu; pathLen = 2 * wcslen(&pathBuffer) + 2; if ( pathLen > BufferSize ) { if ( RetSize ) *RetSize = pathLen; result = 0x84Bu; } else { wcscpy(Buffer, &pathBuffer); result = 0; } return result;}

通過wmic查看命令行參數為svchost.exe -k netsvcs的進程pid

打開OllyDbg,點擊file->attach,附著到svchost.exe進程上,View->Executable modules雙擊netapi32,在cpu指令窗口右鍵選Search for查找exec(label) in current module,找到函數NetpwPathCanonicalize,地址為71C44A3E,在此處設下斷點。

回到CPU指令窗口運行程序,然後攻擊機Metasploit載入ms08_067_netapi模塊並exploit

分析環境中的svchost程序會中斷在 NetpwPathCanonicalize 函數的入口地址處。結合IDA pro對 NetpwPathCanonicalize 的流程分析,在 地址處將調用下一級函數 CanonPathName,在此地址設下斷點。

運行到此斷點,然後跟蹤函數 CanonPathName,傳入參數如下所示:

00F0F8FC 00157570 ;指向prefix,值為x5C0,即Unicode""00F0F900 001572F4 ;指向待整理路徑00F0F904 02132E80 ;指向輸出路徑的buffer00F0F908 000003F9 ;輸出buffer的長度00F0F90C 00000000 ;WORD Flag保留字,值為0

從上兩個函數的參數傳遞可以看出,函數 CanonPathName 進行路徑整理,然後再保存到預先分配的輸出路徑緩衝區buffer中。

下一步分析該函數具體是如何處理待整理路徑的。

在OD中查看待整理路徑的結構,路徑是Unicode字元串,以【x5Cx00】(Unicode字元「」)開始,【x00x00】結束,中間包含一些隨機的大小寫字母,較長一段不可顯示的字元是經過編碼的Shellcode,其中最關鍵的是兩個連在一起的父目錄相對路徑【....】。整個待整理路徑形如:

******....***

在待整理路徑所在內存地址000C0F50處4位元組上設內存訪問斷點

按F9運行,會中斷3次,前兩次分別是檢查待整理路徑的第一個字元和調用wcslen函數,第三次是在調用wcscat函數。分析第三次傳入棧中兩個參數

第一個是strDestination,指向一段以【x5cx00】開頭的內存空間;第二個是strSource,指向上述待整理路徑前兩位元組【x5cx00】後的內容。

程序把待整理路徑全部複製到strDestination,即0x001572F6處。在此4位元組設斷點,類型選擇"Hardware, on access"Word。

按F9運行,中斷多次後停在內存0x77bd4d36處,通過棧可知此處屬於wcscpy函數。此處調用該函數進行第一次路徑規範化。如圖

當前參數 strSource 指向【..***】,參數 strDestination 指向 strTemp 的第一個字元【】。顯然,這次路徑規範化即把待整理路徑中第一個字元【】和第一個【..】相對路徑之間的內容拋棄。

第一次規範化後,待整理路徑形如

..***

由於還有【..】,還需要進行一次規範化,而這第二次規範化正是玄機所在。

正如前面所提到的,當【..】在源字元串開頭的時候,在開始查找時,比較的字元位於緩衝區之外導致了向前的溢出。

有點難,以後深入。

參考

1. MS08-067漏洞分析1.漏洞原因分析-豆丁網

2.《Metasploit滲透測試魔鬼訓練營》機械工業出版社 諸葛建偉等著

推薦閱讀:

TAG:系统漏洞 |