In March 2020’s Patch Tuesday, Microsoft stated that it is aware of a wormable vulnerability, CVE-2020-0796 (aka SMBGhost) in SMB (Server Message Block) 3.1.1 affecting Windows 10 (1903 & 1909), and Windows Server 2019 (1903 & 1909) systems.
SMBGhost is an integer overflow vulnerability in the SMB driver handling the compression header which allows allocation of buffers of incorrect size leading to buffer overflows. This vulnerability exists in the way the Srv2DecompressData() function handles SMB2_COMPRESSION_TRANSFORM_HEADER when the decompress message size is chosen resulting in integer overflow/underflow.
Microsoft has assigned Critical severity to this vulnerability. Another critical preauthentication Remote Code Execution (RCE) vulnerability in SMB version 1.0 (CVE-2017-0144) was exploited by WannaCry ransomware in 2017.
At the time of writing the blog, around 70000 publicly-accessible machines were vulnerable as shown in Figure 1 (note, a few of the exposed machines might merely be SMB honeypots).
SMB is a client-server communication protocol used for sharing access to printers, files and other resources on the network. It is mostly used in organizations to share network drives and directories. Currently SMB versions 2.0 and 3.0 are in use. Since it’s an application layer protocol, it relies on lower-level protocols in the TCP/IP stack for transport and is often used with NetBIOS over TCP/IP.
Microsoft: “SMB 3.0 is capable of detecting man-in-the-middle attacks that attempt to downgrade the SMB 2.0 or SMB 3.0 protocol or the capabilities that the client and server negotiate. When such an attack is detected by the client or the server, the connection is disconnected.”
SMB Dialects are different versions of SMB with different capabilities. Client and Server negotiate each other’s capabilities with negotiation protocols and choose the highest dialect available for data exchange. A typical SMB negotiation is shown in Figure 2.
The first SMB_COM_NEGOTIATE request negotiates the dialect for SMB by sending a list of dialects the client supports. The server then chooses the highest SMB dialect. In this case it’s SMB2 and it sends a response with the SMB2 NEGOTIATE response with a dialect selected as 0x02FF.
SMB Client sends a Negotiate Protocol Request to the SMB server to communicate dialects and negotiates contexts of SMB2 supported by the client, as shown in Figure 3.
SMB Server chooses the highest dialect supported and sends a Negotiate Context as supported by the server through a Negotiate Protocol Response, as shown in Figure 4.
SMB 3.1.1 is supported from Windows 10 and Windows Server 2016 onwards. This has introduced new capabilities to the SMB protocol like encryption and signed algorithms, etc.
New Feature in Windows 19H1 Update
In the Windows 19H1 update, Microsoft introduced two new features: compression and netname without changing the dialect. As the name suggests, the compression capability is used to compress the data to be sent across the network if it exceeds a defined size. As of now, users do not have the option to force compression for each connection.
The vulnerability is triggered when the vulnerable code tries to parse the specially-crafted compressed packet to decompress the data. The vulnerable function Srv2DecompressData() is called when the SMB server/client receives data with the beginning with the magic number 0x424D53FC (i.e. little-endian 0xFC, ‘S’, ‘M’, ‘B’), which is the protocol ID that represents the compression header. The SMB2 COMPRESSION_TRANSFORM_HEADER is shown in Figure 6.
The data from the network is directly copied into the Transform Header structure. The Srv2DecompressData() function parses to the compressed bytes and determines the uncompressed data size from the header to decompress the compressed data. Values from the fields OriginalCompressedSegmentSize (described as the “size, in bytes, of the uncompressed data segment”) and Length (described as the “length, in bytes, of the compressed payload”) are added together to determine the size of allocation to decompress the compressed data.
The highlighted field in Figure 7, “OriginalSize” in the Compression Transform Header has a very large value. The four bytes are FF FF FF FF which is equivalent to 4294967295 (232-1) as an UNSIGNED INT. This value is added with the Offset value in allocating the size of the destination buffer.This overflows the DWORD allocation size and results in the creation of a small destination buffer, thus making the SMBCompressionDecompress function write the decompressed data beyond the boundary of the allocated buffer causing an out-of-bounds overwrite.
Figure 8 below shows the Compression Transform Header passed to Srv2DecompressData() function.
Figure 9 below shows the OriginalCompressedSegmentSize (0xFFFFFFFF) in the rax register and Length (0x3E8) in the rcx register. The next instruction to be executed is add, which would add these values and store the result in the ecx register.
Figure 10 shows the result of the add operation stored in the rcx register, getting passed to the SrvNetAllocateBuffer() function. As a result of integer overflow, the result of the add operation is stored in the rcx register and the overflow bit is stored in the Auxiliary Flag (af).
The Srv2DecompressData() function computes the size of the actual compressed data. This will cause an underflow when the value of data length is greater than the size of the Compression Transform Header and the total size combined. When the computed value is passed to the function as a parameter, it causes an Out-Of-Bounds (OOB) read at SmbCompressionDecompress() as the buffer pointing to read the compressed data is now pointing at the end of the buffer.
Combining both the OOB read and write, an adversary can build a sophisticated remote code execution exploit or a memory leak exploit.
There are multiple Proofs of Concept (PoCs) available online for CVE-2020-0796. The PoC we executed crashed the machine showing a Blue Screen Of Death (BSOD).
Figure 12 shows the remote WinDbg kernel debugging output for the target machine .
Figure 13 shows the function call stack during BSOD analysis. Srv2DecompressData() calls SmbCompressionDecompress() to decompress data using the selected compression-decompression algorithm, which in turn calls RtlDecompressBufferXpressLz() which causes the crash due to an access violation as a result of allocation of a buffer of insufficient size.
Figure 14 shows the mov instruction which caused the crash during the decompression operation.
Figure 15 shows user controlled data passed to the SmbCompressionDecompress() function along with size of data which can be observed during the crash. This can be used to identify positions of interest to develop a full blown attack.
The vulnerable function Srv2DecompressData() has been patched in the srv2.sys driver. This driver is responsible for the vulnerable code on the SMB server side. Lots of code blocks are added in the patch as can be seen in Figure 16 below.
In the patched code, as shown above in Figure 17, a safe arithmetic routine RtlULongAdd() is invoked to add OriginalCompressedSegmentSize and Length values in order to mitigate the overflow.
Along with that, there is another safe arithmetic routine RtlULongSub() that has been introduced while calculating the size of allocation to decompress the compressed data to mitigate the underflow as shown in Figure 18. A few code blocks of the Windows TraceLogging API are included as well as shown in Figure 19.
Exploitation of CVE-2020-0796 leading to a DoS is pretty easy and straightforward but exploiting it for remote code execution could be challenging as Windows has improved its memory defenses. Remote code execution can be achieved through defeating Kernel Address Space Layout Randomisation (KASLR) or by chaining with other memory leakage vulnerabilities. Although this vulnerability could be wormable in theory, we opine that it cannot be as dangerous as the WannaCry case given the complications with and low probability of chaining multiple vulnerabilities to gain RCE.
The patch is available for this vulnerability. Update your OS to install the security patch.
If you are unable to install the updates for any reason, you can disable compression to block adversaries from attacking your SMBv3.1.1 server with the powershell command below:
Set-ItemProperty -Path “HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters” DisableCompression -Type DWORD -Value 1 -Force
Note that the workaround doesn’t stop adversaries from attacking your SMB Client by tricking you into connecting to their malicious servers which respond with crafted packets. It is advised to block SMB outbound traffic from your local network to WAN. Also, it is highly recommended to ensure that connections from the internet are not allowed to SMB services within your enterprise environment.
Some best practices to follow
- Turn on your Firewall at all times
- Avoid using the administrator account as the main account on critical machines
- Ensure your systems are up-to-date for OS patches and allow updates to install by default
- Use a reputed security product like K7 Endpoint Security and K7 Total Security to stay protected from the latest threats
- Avoid exposing your SMB services to the internet unless otherwise required
K7 Security products actively detect attempts to exploit the SMBGhost vulnerability (CVE-2020-0796) under IDS (Rule:intrusion attack – id:000200E8).