20200310,microsoft透露了一个smb v3协议漏洞。
20200312,microsoft出补丁。
漏洞命名:smbghost
Microsoft Server Message Block 3.1.1(SMBv3)协议处理某些请求的方式中存在远程执行代码漏洞,可以在目标smb服务器或客户端上执行代码。
为了利用针对服务器的漏洞,未经身份验证的攻击者可以将特制数据包发送到目标SMBv3服务器;若要利用针对客户端的漏洞,未经身份验证的攻击者将需要配置恶意的SMBv3服务器,并诱使用户连接到该服务器。
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
////至发文(20200322)暂未发现公开EXP。
环境win10x64-1903专业版,关闭防火墙,关闭自动更新!
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导致误报。
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
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." }
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的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
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
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端口
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在计算偏移量字段的同时计算压缩缓冲区的大小。
这两个函数是安全的,可在运行时检查整数上溢/下溢出。
继ms17-010,cve-2019-0708后,又一个蠕虫级RCE漏洞,期待EXP!
//附件是srv2.sys和符号表文件
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