CVE-2020-0796 SMB Ghost 整数溢出漏洞
漏洞位置:SMB服务驱动srv2.sys和srvnet.sys
漏洞原因:用户态可控数据传入内核,SMB在解压数据包的时候使用客户端传过来的长度进行解压时,并没有检查长度是否合法,导致整数溢出,最后通过构造payload,实现任意地址写,替换token权限提权
漏洞分析#
SMB进程权限#
SMB服务使用端口为139和445端口,可以查到对应进程为PID,也就是权限为system的System.exe进程
因此进程token即为System token
SMB格式#
SMB Compression Transform Header的结构
- ProtocolId :4字节,固定为0x424D53FC
- OriginalComressedSegmentSize :4字节,原始的未压缩数据大小
- CompressionAlgorithm :2字节,压缩算法
- Flags :2字节,详见协议文档
- Offset/Length :根据Flags的取值为Offset或者Length,Offset表示数据包中压缩数据相对于当前结构的偏移
漏洞位置#
OriginalCompressedSegmentSize和Offset/Length是由用户控制的数值,且为32位长度,在函数Srv2DecompressData中,由于需要先申请一块内存,因此调用SrvNetAllocateBuffer申请内存,长度参数为下面红框相加部分,由于没有检查溢出(OriginalCompressedSegmentSize+Offset),因此溢出后可以分配到一个很小的空间
exp分析#
- 创建SMB Server的连接
- 获取自身token数据结构中privilege成员在内核中的地址tokenAddr
- 发送畸形数据触发漏洞,包含tokenAddr、权限数据、占位数据
- 触发漏洞,修改tokenAddr地址处权限数据,提升自身权限
- 控制winlogon,创建System权限shell
创建SMB连接#
1 | if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { |
获取自身进程token地址#
1 | ULONG64 get_process_token() { |
准备payload#
1 | ULONG buffer_size = 0x1110; |
构造并发送压缩后的数据#
1 | int send_compressed(SOCKET sock, unsigned char* buffer, ULONG len) { |
注入winlogon进程,开启shell#
1 | void inject(void) { |
利用分析#
分配内存数据#
1 | /* SMB Header */ |
溢出构造:0xffffffff
+0x10
溢出为0xf
0x1ff2ffffbc
0xf对应于0x1100的表项,但其实分配内存大于0x1100,为0x1100 + 0xE8 + 2*(MmSizeOfMdl + 8)
最后返回的return_buffer是一个初始化后的内存数据结构
分配后的结构如下:
return_buffer+0x18指向了0x50处的位置
解压payload#
解压函数参数含义如下:
1 | SmbCompressionDecompress(CompressAlgo,//压缩算法 |
通过解压,将payload解压到*(return_buffer+0x18)+0x10位置,也就是图中0x60所指位置
移动payload实现攻击#
1 | memmove((alloc_buffer+0x18),SMB_payload,offset) |
此时alloc_buffer
指向tokenAddr地址,而SMB_payload则是对应的权限数据,因此可以实现替换token权限攻击
为什么是0x1ff2ffffbc#
tokenAddr+0x40是权限位置,控制当前进程权限
System进程token+0x40位置的值为0x1ff2ffffbc,因此替换后就使得当前进程拥有了System进程权限,继而通过注入其他进程获取系统权限的cmd。