EternalBlue工具漏洞利用細節分析
前言
EternalBlue(永恆之藍)據稱是方程式組織在其漏洞利用框架中一個針對SMB服務進行攻擊的模塊,由於其涉及漏洞的影響廣泛性及利用穩定性,在被公開以後為破壞性巨大的勒索蠕蟲WannaCry所用而名噪一時。360威脅情報中心對於WannaCry的活動持續地進行著監控,我們看到的趨勢是WannaCry的感染量還在增加,說明作為蠕蟲主要傳播手段的EternalBlue相應的漏洞還大量存在著。但是,對於EternalBlue這個攻擊利器本身的技術分析在公開渠道上看到的討論其實並不充分,本文嘗試通過一個較完全的分析梳理相關的細節,揭示其成因和相應的利用技巧。
測試環境
對於EternalBlue的分析是在一個相對簡單的環境中進行的,執行攻擊的系統為一個Win7機器,目標機器也是Win7 32位系統,沒有安裝EternalBlue相關的補丁,srv.sys文件的版本為6.1.7601.17514,srvnet.sys的版本為6.1.7601.17514。本文中所有的調試器中代碼截圖都對應上述的版本,不同版本的文件在代碼本身或偏移可能不同,但整體的執行邏輯應該差不多。
漏洞
根據我們的分析,EternalBlue達到其攻擊目的事實上利用了3個獨立的漏洞:第一個也就是CVE-2017-0144被用於引發越界內存寫;第二個漏洞則用於繞過內存寫的長度限制;第三個漏洞被用於攻擊數據的內存布局。下面重點介紹一下前兩個漏洞,第三個漏洞會在內存布局的過程中提到。
漏洞1
首先是EternalBlue工具中使用到的主體漏洞,該漏洞也是Eternalblue的核心部分,編號為CVE-2017-0144。漏洞通過SMB協議的SMB_COM_TRANSACTION2命令觸發,該命令說明如下所示:
當該數據包中包含對應的FEA LIST時,SMB服務中會將FEA LIST轉換為對應的NTFEA LIST,其對應的數據結構並不公開,如下所示為趨勢團隊分析出的對應的FEALIST結構。
入口處理函數為SrvSmbOpen2,其中漏洞出現在函數SrvOs2FeaListToNt中:
如下所示為對應的漏洞函數SrvOs2FeaListToNt,用於實現FEA LIST轉換為對應的NTFEA LIST,函數調用SrvOs2FeaListSizeToNt計算FEALIST的長度,但是該函數存在漏洞導致在特定的情況下,攻擊者可以偽造超長的size,從而導致在之後的SrvOs2FeaToNt轉換中導致pool溢出。
進入導致漏洞的SrvOs2FeaListSizeToNt函數,該函數會計算對應的FEA LIST的長度並隨後對長度進行更新,該長度一開始為DWORD類型的,之後的長度更新代碼中計算出的size拷貝回去的時候是按WORD進行的拷貝,此時只要原變數a中的初始值大於FFFF,即為10000+,該函數的計算結果就會增大。
該賦值中如下所示esi變成了si,此時如果eax高位中的數據不為零,則將返回的超長的size。
如下圖所示為對應發送的該數據包,可以看到該請求數據包的長度為103d0,其中對應的FEALIST的長度為10000。
如下圖所示,eax為鏈表的開頭,其指向了FEA LIST的總長度,即10000,esi為遍歷之後的鏈表尾部,eax-esi=ff5d,為實際對應的長度,但是更新長度的mov指令中esi變成了si,由於eax中的值為10000,原本應該被賦值為ff5d的eax,變成了1ff5d。
之後在緊接著的函數SrvOsFeaToNt中,由於使用了錯誤的長度進行memmove從而導致溢出。
下圖為其中的複製導致越界寫,長度為a8,可以看到正常請求應該是在86535000這個srv.sys對象SMB buffer中,由於長度過長導致對srvnet.sys分配的buffer越界寫。
Enternalblue中通過內存布局,將srvnet對象buffer穩定的分配到srv拷貝對象buffer之後,如下圖所示為越界寫時的內存情況。
Srvnet 對象buffer中包含兩個重要的域:
1.一個指向指定結構(srvnet_recv)的指針(即上圖中的8834e4c0,被ffdff020覆蓋),該指針將會在smb(srnet)連接結束或斷開時被用於定址函數地址。
2.一個用於接收緩衝區的MDL(即上圖中的86546160,被ffdfef80覆蓋)
因此覆蓋並控制MDL將導致之後的tcp 棧實現任意寫入偽造對象的操作,覆蓋並控制該指針可用於將其指向一個攻擊者控制的偽造對象,此時斷開smb(srvnet)連接即可導致代碼執行。
如下圖所示,MDL複寫為ffdfef80後,緊接著Eternalblue發送的shellcode就會被寫入到ffdfef80+0x80的位置,即ffdff000。
可以看到此時的調用棧:
寫入的地址ffdff000是系統預留的用於保存系統信息的地址,並且可執行。
被寫入到ffdff000地址的是一個srvnet_recv的結構(該結構不公開)和緊隨其後的shellcode,該結構用於smb(srnet)結束或斷開連接的時候通過SrvNetWskReceiveComplete調用SrvNetCommonReceiveHandler 。SrvNetCommonReceiveHandler 根據srv_recv中的指針此處為下圖中的poi(ffdff190(ffdff020(被覆蓋的對應指針)+0x16c)+4)獲取到對應的函數並調用,地址即我們偽造的shellcode的地址(ffdff1f1)。
漏洞2
如上述漏洞所示可以導致一次越界寫,但其前提是FEA LIST的長度必須大於10000,通過分析可以發現FEA LIST只存在於SMB_COM_TRANSACTION2命令的子命令中,而該命令的數據結構如下:
TotalDataCount(數據包總長度)是USHOER類型的,即最大值只能為FFFF,那在這個地方EternalBlue是如何發送的長度大於FFFF的SMB_COM_TRANSACTION2子命令請求的呢?
通過抓包可以發現此處發送的並不是SMB_COM_TRANSACTION2子命令的請求包,而是SMB_COM_NT_TRANSACT子命令的請求包:
如下圖所示SMB_COM_NT_TRANSACT子命令中TotalDataCount的類型為ULONG,支持發送大於FFFF長度的數據包。
但是SMB_COM_NT_TRANSACT本身是不支持FEA LIST的,這裡就涉及到EternalBlue中使用到的第二個漏洞。
SMB的子命令中存在一個名為TRANSACTION系列的命令:
SMB_COM_TRANSACTION: 用於和郵槽、命名管道進行通信
SMB_COM_TRANSACTION2: 用於打開或創建一個共享文件或文件夾,設置它們的擴展屬性
SMB_COM_NT_TRANSACT: 用於打開或創建一個文件或文件夾,並應用擴展屬性EA或安全描述符SD
其中產生漏洞的即為對應的SMB_COM_TRANSACTION2命令。
對於TRANSACTION系列的命令如果發送的長度過大,SMB會將該請求包拆分成**Second的形式進行發送,如下所示為其相應的**Second系列的命令:
SMB_COM_TRANSACTION
SMB_COM_TRANSACTION_SECONDARY
SMB_COM_TRANSACTION2
SMB_COM_TRANSACTION2_SECONDARY
SMB_COM_NT_TRANSACT
SMB_COM_NT_TRANSACT_SECONDARY
服務端根據SMB請求頭部的TIP,PID,UID,MID確定哪一個**Second屬於對應的transtion,而服務端根據最後一個**Second確定對應的transtion類型,即如果最後一個**Second為SMB_COM_TRANSACTION2_SECONDARY,就按SMB_COM_TRANSACTION2來處理。
如下圖為處理對應**Second的邏輯,對於一個transaction,如果沒有發送完,後續會跟上對應的**Second數據包,服務端不會檢查對應的**Second類型,只要保證其TIP,PID,UID,MID匹配,服務端就會將這些數據重新組裝還原成一個transaction,而類型由最後一個**Second決定。
因此,為了發送一個長度為0x10000的SMB_COM_TRANSACTION2,首先發送一個長度為103d0(FEA LIST:1000)SMB_COM_NT_TRANSACT,之後發送一系列SMB_COM_TRANSACTION2_SECONDARY數據包,只要保證TIP,PID,UID,MID一致,服務端最後就會將其當做一個SMB_COM_TRANSACTION2來處理,而此時其長度103d0。
由於SMB會等待最後一個**SECOND數據包到來才生成最後的transaction,因此EternalBlue可以在此期間發包對目標設備的內存進行部署,之後再發送最後一個數據包從而觸發漏洞越界寫。
內存布局的構建
如上述分析,利用漏洞會觸發溢出導致越界寫,而EternalBlue中對於該漏洞的利用思路和大多數的pool越界寫是一致的:
1.在內存中spray一系列srvnet的對象buffer
2.釋放掉其中的空間,以便於srv的對象buffer進行佔位
3.srv對象buffer佔位
4.發包越界寫srvnet的對象buffer
5.觸發代碼執行
srvnet對象spray
但是這裡和一般的內核漏洞的利用存在一個很大的區別,就是我們的環境是遠程的。通常的本地內核漏洞利用的時候我們可以從容地選擇進行spray的內核對象,但是對於遠程的環境而言,內核對象的選擇及對應的控制就要小很多。
EternalBlue中用於被覆蓋的對象為srvnet buffer,其中的對象包含兩個重要的結構:
1.一個指向指定結構的指針,通過覆蓋它可以將其指向一個偽造的結構,從而實現後續的代碼執行。
2.一個接受MDL的緩衝區,通過覆蓋它可以保證將後續發送的偽造結構及shellcode寫到指定的區域。
微軟提供了SMB 2直接支持TCP的通信方式,可以通過該方式來創建srvnet緩衝區。
如下圖所示srvnet對象的spray過程,生成的大小依賴於前四位元組。
srv對象spray
srv對象是通過釋放後重申請的方式獲取的地址空間,但是SMB中如何通過遠程方式穩定的申請並釋放一段pool內存了?這就涉及到EternalBlue中使用的第3個漏洞。
該漏洞出現在SMB_COM_SESSION_SETUP_ANDX命令中:
該命令的請求依賴於WordCount的值來確定具體的請求格式,當為12時格式如下圖所示,當為13時紅框中的變數會有所區別。
直接借用網上逆向簡化後的一段代碼,如下所示:如果發送的代碼中WordConut為12,包含CAP_EXTENDED_SECURITY欄位,但卻沒有FLAGS2_EXTENDED_SECURITY欄位,將會導致伺服器將以處理13類型請求的方式去處理類型12的請求包,從而進入錯誤的函數GetNtSecurityParameters流程中。
GetNtSecurityParameters會檢查對應的請求中的參數,函數參數中的v70為通過wordcount和Bytecount計算出來的一個size。
GetNtSecurityParameters函數中的計算如下所示:
該參數返回後作為SrvAllocateNonPagedPool的參數分配一段pool。
因此利用該漏洞將12類型的請求包通過13類型進行處理,由於兩種類型的請求包格式不一致,通過控制請求包指定偏移的數據,即可以控制SrvAllocateNonPagedPool創建的pool的大小,可以使用以下的斷點監控該過程:
bp GetNtSecurityParameters+0x1AC ".printf"GetNtSecurityParameters1n";r;.echo;?cx-si+bx+1d;g;"nbp SrvAllocateNonPagedPool+0x10 ".printf"SrvAllocateNonPagedPool NonPageSize:%pn",ecx;g;"nbp SrvAllocateNonPagedPool+0x15C ".printf"SrvAllocateNonPagedPool alloc Nopage:%pn",eax;g;"nbp BlockingSessionSetupAndX+0x7C0 ".printf"BlockingSessionSetupAndX doublen";g;"n
如下圖所示即為通過斷點監控到的非法size生成的過程,通過構造畸形數據包,包含數據87f8,漏洞觸發後識別出該錯誤的偏移,計算最後會分配一段大小為10fec大小的pool。
通過斷開對應的該命令請求,可以導致之前分配的10fec大小的pool被釋放,從而在地址空間中生成一個hole,該hole之後會被srv對象buffer來填充。
現在知道了如何在內存中穩定的spray一段連續的srvnet的對象buffer,以及如何開闢並釋放一段指定大小的空間,內存布局的基本條件已經具備,可以看到具體的布局流程到最後的觸發執行過程如下:
1.通過SMB_COM_NT_TRANSACT發送一段FEA LIST長度滿足0x10000的數據包
2.發送後續的SMB_COM_TRANSACTION2_SECONDARY,這將導致smb服務將SMB_COM_NT_TRANSACT當做SMB_COM_TRANSACTION2處理,但是最後一個SMB_COM_TRANSACTION2_SECONDARY留置最後。
3.通過smb 2協議進行srvnet對象的spray
4.通過SMB_COM_SESSION_SETUP_ANDX漏洞在srvnet對象之後分配一段大小和srv對象大小几乎一致的pool內存
5.通過smb 2協議繼續進行srvnet對象的spray,以確保srvnet位於srv對象之後
6.斷開連接導致第4步開闢的pool內存釋放,生成一個hole
7.發送最後一個SMB_COM_TRANSACTION2_SECONDARY,由於大小一致,該數據包會填補生成的hole,並觸發漏洞導致之後的srvnet對象buffer中的MDL和指針被修改,此時後續發送的數據將拷貝到ffdff000的位置。
8.斷開所有連接,觸發srvnet_recv指向的shellcode執行
可以通過以下斷點監控利用時內存的釋放和分配(主要是srv,srvnet對象):
bp SrvAllocateNonPagedPool+0x10 ".printf"SrvAllocateNonPagedPool NonPageSize:%pn",ecx;g;"nbp SrvAllocateNonPagedPool+0x15C ".printf"SrvAllocateNonPagedPool alloc Nopage:%pn",eax;g;"nbp SrvFreeNonPagedPool+0x3 ".printf"SrvFreeNonPagedPool free Nopage:%pn",eax;g;"nbp BlockingSessionSetupAndX ".printf"BlockingSessionSetupAndXn";g;"nbp SrvNetAllocateNonPagedBufferInternal ".printf"AllocateNonPaged NonPagedBufferSize:%pn",poi(esp+8);g;"nbp SrvNetAllocateNonPagedBufferInternal+0x179 ".printf"AllocateNonPaged NonPagedBufferAddress:%pn",eax;g;"nbp SrvNetFreeNonPagedBufferInternal ".printf"SrvNetFreeNonPagedBufferInternal free NonPageBufferAddress:%pn",poi(esp+4);g;"nba e1 srvnet!SrvNetWskReceiveComplete+0x13 ".if(poi(esi+0x24) == ffdff020) {} .else {gc}"n
如下圖所示即為整體監控到的數據包於內存中的布局情況,其中867bb000處為對應的srv buffer對象,之後867cc000上的srvnet buffer對象將會被覆蓋如下所示:
以上為EternalBlue利用過程中內存布局及對應發送數據包的一個概述,但是其內部其實還有一些細節可供深入挖掘。由於作者水平有限,有什麼錯誤歡迎大家指正。
參考資料
http://bobao.360.cn/learning/detail/3738.html
https://github.com/worawit/MS17-010
http://blog.trendmicro.com/trendlabs-security-intelligence/ms17-010-eternalblue/
推薦閱讀:
※做網路安全怎麼避免犯法?
※2013 年中國互聯網安全環境面臨的主要威脅有哪些?
※為什麼電子郵件允許冒充發件人?
※說人話系列:從Intel處理器漏洞談相關冷知識
※看完這個回答,嚇得我又運行了一遍Revoke China Cert