一步一步教你漏洞挖掘之Windows SMB Ghost CVE-2020-0796(一)
“ 2020年3月11日,微軟公布了SMBGhost漏洞的相關信息,這是內核驅動srv2.sys中SMBv3.1.1消息解壓縮過程中的一個整數溢出漏洞。到3月底就出現了利用該漏洞實現本地提權(LPE)的技術研究,之后就是國內外各路大神都發布了相關研究成果。但是針對該漏洞的遠程利用似乎只有 Ricerca Security 的研究人員在4月發布了其研究成果。下面主要是基于 Ricerca Security 的研究成果和利用腳本,對該漏洞進行復現、成因分析和利用分析。”
漏洞復現
0x01 測試環境
目標系統:Windows 10 1909 64位 build 18363.418

調試方式:遠程內核調試(net 模式)
虛擬機配置:


調試器Windbg配置:


0x02 測試過程
漏洞觸發過程非常簡單,只要發送一個包含壓縮數據的SMB v3的數據報就可以導致目標藍屏崩潰。使用的漏洞觸發腳本:
https://github.com/eerykitty/CVE-2020-0796-PoC
發送的 SMB 數據包格式如下:


漏洞poc中對SMB壓縮數據結構(offset)進行惡意賦值:
def _compress(self, b_data, session): header = SMB2CompressionTransformHeader() header['original_size'] = len(b_data) header['offset'] = 4294967295 header['data'] = smbprotocol.lznt1.compress(b_data) return header
調試器中的崩潰現場:
PAGE_FAULT_IN_NONPAGED_AREA (50)Invalid system memory was referenced. This cannot be protected by try-except.Typically the address is just plain bad or it is pointing at freed memory.……rax=ffffba86ed2f9d58 rbx=0000000000000000 rcx=ffffba86ed2f9d50rdx=0000000000000001 rsi=0000000000000000 rdi=0000000000000000……nt!RtlDecompressBufferLZNT1+0x57:fffff806`356f0127 0fb71e movzx ebx,word ptr [rsi] ds:00000000`00000000=????……STACK_TEXT:……nt!RtlDecompressBufferLZNT1+0x57nt!RtlDecompressBufferEx2+0x66srvnet!SmbCompressionDecompress+0xddsrv2!Srv2DecompressData+0xe1srv2!Srv2DecompressMessageAsync+0x1e……
漏洞成因分析
根據崩潰的位置,我們通過逆向回溯,可以一步一步找到漏洞的成因,首先看到nt!RtlDecompressBufferLZNT1+0x57處的內存訪問異常,是由于rsi寄存器地址異常導致的,所以可以通過追溯rsi的數據流,來尋找污點源:

從IDA可以看到,rsi的值來自nt!RtlDecompressBufferLZNT1的第一個參數r8,由于其父函數是nt!RtlDecompressBufferEx2,分析父函數中相關代碼:

r8的值來自nt!RtlDecompressBufferEx2的第二個參數r9,繼續追溯其父函數,發現是srvnet!SmbCompressionDecompress,繼續分析父函數中相關代碼:

r9的值來自r15,在向前追溯可以發現r15的值其實來自srvnet!SmbCompressionDecompress的第4個參數rdx,最后我們追溯到srv2!Srv2DecompressData,可以看到rdx是兩個數相加的結果:

其實到這里就是漏洞的關鍵點(溢出就發生在該函數),從srv2!Srv2DecompressData的函數名可知該函數的作用就是處理SMB數據包中的壓縮數據,用IDA對該函數進行分析(其中關鍵數據結構直接搬運的 ZecOps 研究團隊的逆向成果)
NTSTATUS Srv2DecompressData(PHEADER Header, SIZE_T TotalSize) { PSRVNET_BUFFER_HDR Alloc = SrvNetAllocateBuffer( (ULONG)(Header->OriginalSize + Header->Offset), NULL); …… NTSTATUS Status = SmbCompressionDecompress( Header->CompressionAlgorithm, (PUCHAR)Header + sizeof(HEADER) + Header->Offset, (ULONG)(TotalSize - sizeof(HEADER) - Header->Offset), (PUCHAR)Alloc->UserBuffer + Header->Offset, Header->OriginalSize, &FinalCompressedSize); ……}
srv2!Srv2DecompressData的第4個參數(第10行)是一個加法表達式:Alloc->UserBuffer + Header->Offset,其中Alloc->UserBuffer是通過SrvNetAllocateBuffer(第3行)動態申請的內存指針,Header->Offset就是數據包中我們設置的異常值0xffffffff,由于在代碼中沒有對Header->Offset所涉及的數學運算進行驗證,于是在函數Srv2DecompressData中出現了一系列可能的溢出點(包括第4、8、9、10行)。
可以通過調試器我們再確認下:

Alloc->UserBuffer就是rax,Header->Offset就是rdx,其值為0xffffffff+0x10,由于沒有對Header->Offset的值進行相關檢查,加法操作導致指針越界,系統訪問非法內存而崩潰。其實用戶可以控制的除了Header->Offset,還有一個是Header->OriginalSize,在后面的利用過程中可以利用。

當然如果想要搞清楚Alloc->UserBuffer的申請過程,需要進入SrvNetAllocateBuffer函數進行分析,該過程比較復雜,而且在調試過程中發現,我自己的虛擬機中并沒有按照 ZecOps 研究團隊分析的那樣執行,后面漏洞利用的時候再做詳細分析吧。
下一篇我們將詳細分析CVE-2020-0796各種利用姿勢。