Windows SMB Ghost(CVE-2020-0796)漏洞分析
1 漏洞介紹
2020年3月10日,微軟在其官方SRC釋出了CVE-2020-0796的安全公告(ADV200005,Microsoft Guidance for Disabling SMBv3 Compression),公告表示在Windows SMBv3版本的客戶端和服務端存在遠端程式碼執行漏洞。同時指出該漏洞存在於MicroSoft Server Message Block 3.1.1協議處理特定請求包的功能中,攻擊者利用該漏洞可在目標SMB Server或者Client中執行任意程式碼。
啟明星辰ADLab安全研究人員在對該漏洞進行研究的過程中發現目前流傳的一些漏洞分析存在某些問題,因此對該漏洞進行了深入的分析,並在Windows 10系統上進行了復現。
2 漏洞復現
採用Windows 10 1903版本進行復現。在漏洞利用後,驗證程式提權結束後建立了一個system許可權的cmd shell,如圖1所示。
圖1 CVE-2020-0796本地提權
3 漏洞基本原理
CVE-2020-0796漏洞存在於受影響版本的Windows驅動srv2.sys中。Windows SMB v3.1.1 版本增加了對壓縮資料的支援。圖2所示為帶壓縮資料的SMB資料包文的構成。
圖 2 帶壓縮資料的SMB資料包文結構
根據微軟MS-SMB2協議文件,SMB Compression Transform Header的結構如圖3所示。
ProtocolId :4位元組,固定為0x424D53FC
OriginalComressedSegmentSize:4位元組,原始的未壓縮資料大小
CompressionAlgorithm:2位元組,壓縮演算法
Flags:2位元組,詳見協議文件
Offset/Length:根據Flags的取值為Offset或者Length,Offset表示資料包中壓縮資料相對於當前結構的偏移
圖 3 SMB Compression Transform Header資料結構
srv2.sys中處理SMBv3壓縮資料包的解壓函式Srv2DecompressData未嚴格校驗資料包中OriginalCompressedSegmentSize和Offset/Length欄位的合法性。而這兩個欄位影響了Srv2DecompressData中記憶體分配函式SrvNetAllocateBuffer的引數。如圖4所示的Srv2DecompressData函式反編譯程式碼,SrvNetAllocateBuffer實際的引數為OriginalCompressedSegmentSize+Offset。這兩個引數都直接來源於資料包中SMB Compression Transform Header中的欄位,而函式並未判斷這兩個欄位是否合法,就直接將其相加後作為記憶體分配的引數(unsigned int型別)。
圖 4 Srv2DecompressData函式的關鍵程式碼
這裡,OriginalCompressedSegmentSize+Offset可能小於實際需要分配的記憶體大小,從而在後續呼叫解壓函式SmbCompressionDecompress過程中存在越界讀取或者寫入的風險。
4 提權利用過程
目前已公開的針對該漏洞的本地提權利用包含如下的主要過程:
(1)驗證程式首先建立到SMS server的會話連線(記為session)。
(2)驗證程式獲取自身token資料結構中privilege成員在核心中的地址(記tokenAddr)。
(3)驗證程式透過session傳送畸形壓縮資料(記為evilData)給SMB server觸發漏洞。其中,evilData包含tokenAddr、許可權資料、溢位佔位資料。
(4) SMS server收到evilData後觸發漏洞,並修改tokenAddr地址處的許可權資料,從而提升驗證程式的許可權。
(5)驗證程式獲取許可權後對winlogon進行控制,來建立system使用者shell。
5 漏洞記憶體分配分析
首先,看一下已公開利用的evilData資料包的內容,如圖5所示。
圖 5 提權poc傳送的帶壓縮資料的SMB資料包
資料包的內容很簡單,其中幾個關鍵欄位資料如下:
OriginalSize:0xffffffff
Offset:0x10
Real compressed data :13位元組的壓縮資料,解壓後應為1108位元組’A’加8位元組的token地址。
SMB3 raw data :實際上是由2個8位元組的0x1FF2FFFFBC(總長0x10)加上0x13位元組的壓縮資料組成
從上面的漏洞原理分析可知,漏洞成因是Srv2DecompressData函式對報文欄位缺乏合法性判斷造成記憶體分配不當。在該漏洞資料包中,OriginalSize 是一個畸形值。OriginalSize + Offset = 0xffffffff + 0x10 = 0xf 是一個很小的值,其將會傳遞給SrvNetAllocateBuffer進行呼叫,下面具體分析記憶體分配情況。SrvNetAllocateBuffer的反編譯程式碼如圖6。
圖 6 SrvNetAllocateBuffer記憶體分配過程
由於傳給SrvNetAllocateBuffer的引數為0xf,根據SrvNetAllocateBuffer的處理流程可知,該請求記憶體將從SrvNetBufferLookasides表中分配。這裡需要注意的是,變數SrvDisableNetBufferLookAsideList跟登錄檔項相關,系統預設狀態下SrvDisableNetBufferLookAsideList為0。
圖 7 SrvDisableNetBufferLookAsideList變數初始化過程
SrvNetBufferLookasides表透過函式SrvNetCreateBuffer初始化,實際SrvNetCreateBuffer迴圈呼叫了SrvNetBufferLookasideAllocate分配記憶體,呼叫SrvNetBufferLookasideAllocate的引數分別為[‘0x1100’, ‘0x2100’, ‘0x4100’, ‘0x8100’, ‘0x10100’, ‘0x20100’, ‘0x40100’, ‘0x80100’, ‘0x100100’]。在這裡,記憶體分配引數為0xf,對應的lookaside表為0x1100大小的表項。
圖 8 SrvNetCreateBuffer反編譯程式碼
SrvNetBufferLookasideAllocate函式實際是呼叫SrvNetAllocateBufferFromPool來分配記憶體,如圖9所示。
圖 9 SrvNetBufferLookasideAllocate反編譯程式碼
在函式SrvNetAllocateBufferFromPool中,對於使用者請求的記憶體分配大小,內部透過ExAllocatePoolWithTag函式分配的記憶體實際要大於請求值(多出部分用於儲存部分記憶體相關資料結構)。以請求分配0x1100大小為例,經過一系列判斷後,最後分配的記憶體大小allocate_size = 0x1100 + E8 + 2*(MmSizeOfMdl + 8)。
圖10 SrvNetAllocateBufferFromPool函式部分反編譯程式碼
記憶體分配完畢之後,SrvNetAllocateBufferFromPool函式還對分配的記憶體進行了一系列初始化操作,最後返回了一個記憶體資訊結構體指標作為函式的返回值。
圖 11 SrvNetAllocateBufferFromPool初始化記憶體資料
這裡需要注意如下的資料關係:SrvNetAllocateBufferFromPool函式返回值return_buffer指向一個記憶體資料結構,該記憶體資料結構起始地址同實際分配記憶體(函式ExAllocatePoolWithTag分配的記憶體)起始地址的的偏移為0x1150;return_buffer+0x18位置指向了實際分配記憶體起始地址偏移0x50位置處,而最終return_buffer會作為函式SrvNetAllocateBuffer的返回值。其記憶體佈局關係如圖12。
圖 12 SrvNetAllocateBuffer(0xf)返回的記憶體資料佈局
6 漏洞記憶體破壞分析
回到漏洞解壓函式Srv2DecompressData,在進行記憶體分配之後,Srv2DecompressData呼叫函式SmbCompressionDecompress開始解壓被壓縮的資料。其函式邏輯如圖13所示。
圖 13 Srv2DecompressData解壓壓縮資料
實際上,該函式呼叫了Windows庫函式RtlDecompressBufferEx2來實現解壓,根據RtlDecompressBufferEx2的函式原型來對應分析SmbCompressionDecompress函式的各個引數。
SmbCompressionDecompress(CompressAlgo,//壓縮演算法
Compressed_buf,//指向資料包中的壓縮資料
Compressed_size,//資料包中壓縮資料大小,計算得到
UnCompressedBuf,//解壓後的資料儲存地址,*(alloc_buffer+0x18)+0x10
UnCompressedSize,//壓縮資料原始大小,源於資料包OriginalCompressedSegmentSize
FinalUnCompressedSize)//最終解壓後資料大小
從反編譯程式碼可以看出,函式SmbCompressionDecompress中儲存解壓後資料的地址為*(alloc_buffer+0x18)+0x10的位置,根據記憶體分配過程分析,alloc_buffer + 0x18指向了實際記憶體分配起始位置偏移0x50處,所以複製目的地址為實際記憶體分配起始地址偏移0x60位置處。
在解壓過程中,壓縮資料解壓後將儲存到這個地址指向的記憶體中。根據evilData資料的構造過程,解壓後的資料為佔坑資料和tokenAddr。複製到該處地址後,tokenAddr將覆蓋原記憶體資料結構中alloc_buffer+0x18處的資料。也就是解壓縮函式SmbCompressionDecompress返回後,alloc_buffer+0x18將指向驗證程式的tokenAddr核心地址。複製過程如圖14所示:
圖 14 解壓複製過程
圖 15 解壓完成後記憶體佈局
繼續看Srv2DecompressData的後續處理流程,解壓成功後,函式判斷offset的結果不為0。不為0則進行記憶體移動,記憶體複製的引數如下:
memmove(*(alloc_buffer+0x18),SMB_payload,offset)
此時alloc_buffer+0x18已經指向驗證程式的tokenAddr核心地址,而SMB_payload此時指向evilData中的許可權資料,offset則為0x10。因此,這個記憶體移動完成後,許可權資料將寫入tokenAddr處。這意味著,SMS Server成功修改了驗證程式的許可權,從而實現了驗證程式的提權!
還有一個細節需要注意,在解壓時,Srv2DecompressData函式會判斷實際的解壓後資料大小FinalUnCompressedSize是否和資料包中原始資料大小OriginalCompressedSegmentSize一致,如圖16所示。
圖 16 Srv2DecompressData檢查壓縮資料大小
按理來說實際解壓後的資料大小為0x1100,不等於資料包中的原始壓縮資料大小0xffffffff,這裡應該進入到後面記憶體釋放的流程。然而,實際上在函式SmbCompressionDecompress中,呼叫RtlDecompressBufferEx2成功後會直接將OriginalCompressedSegmentSize賦值給FinalUnCompressedSize。這也是該漏洞關於任意地址寫入成功的關鍵之一。
圖 17 SmbCompressionDecompres賦值FinalUnCompressedSize
7 漏洞修復建議
CVE-2020-0796是記憶體破壞漏洞,精心利用可導致遠端程式碼執行,同時網路上已經出現該漏洞的本地提權利用程式碼。在此,建議受影響版本Windows使用者及時根據微軟官方漏洞防護公告對該漏洞進行防護。
8 參考連結
1. https://fortiguard.com/encyclopedia/ips/48773
2. https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/ADV200005
3. https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-0796
4. https://www.catalog.update.microsoft.com/Search.aspx?q=KB4551762
5. https://github.com/danigargu/CVE-2020-0796
6. https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/5606ad47-5ee0-437a-817e-70c366052962
7. https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-rtldecompressbufferex2
相關文章
- SMB遠端程式碼執行漏洞CVE-2020-0796安全通告2020-03-13
- Windows PrintDemon提權漏洞分析2020-05-25Windows
- GHOST漏洞可能影響WordPress和PHP應用2017-06-06PHP
- win10 smb提示沒有許可權怎麼辦_windows 10如何開啟smb教程2020-04-26Win10Windows
- SMB協議2015-06-03協議
- 在SATA模式下安裝windows xp的方法(非GHOST)2015-01-04模式Windows
- Ghost Push —— Monkey Test & Time Service病毒分析報告2020-08-19
- Windows五次Shift漏洞2020-08-29Windows
- 【漏洞分析】KaoyaSwap 安全事件分析2022-08-28事件
- BlueKeep 漏洞利用分析2019-09-20
- XSS漏洞分析2017-11-27
- 漏洞分析 | Dubbo2.7.7反序列化漏洞繞過分析2020-07-02
- SMB與CIFS 445埠2019-03-12
- SMB服務詳解2018-09-06
- SMB協議詳解2016-11-23協議
- Windows核心提權漏洞CVE-2014-4113分析報告2020-08-19Windows
- PfSense命令注入漏洞分析2020-08-19
- SSRF漏洞簡單分析2020-07-16
- JSON劫持漏洞分析2018-05-17JSON
- 從exp入手分析漏洞2016-07-26
- tp5漏洞分析2024-06-30
- Macrium Reflect 取代ghost2022-05-16Mac
- ghost模板總結2015-09-04
- Kali中SMB爆破工具————acccheck2018-06-08
- SMB/CIFS協議解析(一)2015-01-23協議
- 漏洞分析——變數缺陷漏洞及通用異常捕獲宣告缺陷漏洞2021-09-01變數
- 軟體漏洞分析技巧分享2020-08-19
- Java安全之Axis漏洞分析2021-11-26Java
- thinkphp3.2.x漏洞分析2024-06-30PHP
- Windows域提權漏洞CVE-2022-26923分析與復現2022-06-23Windows
- 在Github上部署Ghost2016-01-20Github
- GHOST使用教程圖解2013-05-26圖解
- win10 ghost備份後提示windows不能啟動最佳解決方法2020-04-10Win10Windows
- Vagrant 開啟 smb 檔案共享2019-11-23
- CIFS檔案的儲存————SMB2017-12-07
- SMB(Server Message Block) Protocal Research2017-05-30ServerBloC
- Ubuntu下配置smb伺服器2015-10-02Ubuntu伺服器
- FreeBSD SMB/CIFS協議(轉)2007-08-11協議