“ 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=ffffba86ed2f9d50
rdx=0000000000000001 rsi=0000000000000000 rdi=0000000000000000
……
nt!RtlDecompressBufferLZNT1+0x57:
fffff806`356f0127 0fb71e movzx ebx,word ptr [rsi] ds:00000000`00000000=????
……
STACK_TEXT:
……
nt!RtlDecompressBufferLZNT1+0x57
nt!RtlDecompressBufferEx2+0x66
srvnet!SmbCompressionDecompress+0xdd
srv2!Srv2DecompressData+0xe1
srv2!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各种利用姿势。
参考
https://github.com/eerykitty/CVE-2020-0796-PoC
https://github.com/chompie1337/SMBGhost_RCE_PoC
https://blog.zecops.com/vulnerabilities/exploiting-smbghost-cve-2020-0796-for-a-local-privilege-escalation-writeup-and-poc/#
https://ricercasecurity.blogspot.com/2020/04/ill-ask-your-body-smbghost-pre-auth-rce.html
由于传播、利用此文档提供的信息而造成任何直接或间接的后果及损害,均由使用本人负责,且听安全团队及文章作者不为此承担任何责任。
点关注,不迷路!
原文始发于微信公众号(且听安全):【经典回顾系列】 一步一步教你漏洞挖掘之Windows SMB Ghost CVE-2020-0796(一)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论