一步一步教你漏洞挖掘之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各种利用姿势。
