SMBleed (CVE-2020-1206), its relation to SMBGhost and how to fix them
The SMBleed vulnerability (CVE-2020-1206) allows an attacker to read uninitialized kernel memory. It happens in the same function as SMBGhost (CVE-2020-0796), a bug in the compression mechanism of SMBv3.1.1, as explained in a previous blog.
So First – How Can You Fix It?
There are several ways to mitigate the risk from the SMBleed vulnerability.
1. Windows Update
The most recommended solution is to apply Windows updates:
Windows Version | KB |
Windows 10 Version 1903 | KB4560960 |
Windows 10 Version 1909 | KB4560960 |
Windows 10 Version 2004 | KB4557957 |
Mitigation through workarounds
However, we realize that applying an update is not always an option. This is why we’ve attached several workarounds, which could help mitigate the risk immediately.
2. Disabling SMB 3.1.1 compression
You can disable compression to block unauthenticated attackers from exploiting the vulnerability against an SMBv3 Server with the following PowerShell command:
Set-ItemProperty -Path “HKLM:SYSTEMCurrentControlSetServicesLanmanServerParameters” DisableCompression -Type DWORD -Value 1 -Force |
Notes:
- No reboot is needed after making the change.
- This workaround does not prevent exploitation of SMB clients; please see item 2 under FAQ to protect clients.
- SMB Compression is not yet used by Windows or Windows Server, and disabling SMB Compression has no negative performance impact.
You can disable the workaround with the following PowerShell command:
Set-ItemProperty -Path “HKLM:SYSTEMCurrentControlSetServicesLanmanServerParameters” DisableCompression -Type DWORD -Value 0 –Force smb |
3. Block TCP port 445 at the enterprise perimeter firewall
TCP port 445 is used to initiate a connection with the affected component. Blocking this port at the network perimeter firewall will help protect systems that are behind that firewall from attempts to exploit this vulnerability. This can help protect networks from attacks that originate outside the enterprise perimeter. Blocking the affected ports at the enterprise perimeter is the best defense to help avoid Internet-based attacks. However, systems could still be vulnerable to attacks from within their enterprise perimeter.
4. Blocking port 445 inside the enterprise where not needed
Where it is not needed, block port 445 on the relevant assets. This will stop lateral movements using these vulnerabilities.
Root cause
The SMBleed vulnerability happens in the Srv2DecompressData function in the srv2.sys SMB server driver, similarly to SMBGhost. It receives the compressed message sent by the client, allocates the required amount of memory, and decompresses the data.
Then, if the Offset field isn’t zero, the Srv2DecompressData function will take the data placed before the compressed data and copy it, as is, to the beginning of the allocated buffer. See appendix for a simplified version of the function.
Exploiting the bug
“The message structure contains fields such as the amount of bytes to write and flags, followed by a variable-length buffer” said the ZecOps researchers. “That’s perfect for exploiting the bug since we can craft a message such that we specify the header, but the variable-length buffer contains uninitialized data.”
As Microsoft mentioned in its advisory, “An attacker who successfully exploited the vulnerability could obtain information to further compromise the user’s system. To exploit the vulnerability against a server, an unauthenticated attacker could send a specially crafted packet to a targeted SMBv3 server. To exploit the vulnerability against a client, an unauthenticated attacker would need to configure a malicious SMBv3 server and convince a user to connect to it.”
In case of a successful decompression, FinalCompressedSize is updated to hold the value of CompressedBufferSize, which is the size of the buffer. Not only this seemingly unnecessary, deliberate update of the FinalCompressedSize value made the exploitation of SMBGhost easier, it also allowed the SMBleed bug to exist.
Basic exploitation POC
Note that ZECOPS POC requires credentials and a writable share, which are available in many scenarios, but the bug applies to every message, so it can potentially be exploited without authentication. Also note that the leaked memory is from previous allocations in the NonPagedPoolNx pool, and since theycontrol the allocation size, they might be able to control the data that is being leaked to some degree.
Source: https://github.com/ZecOps/CVE-2020-1206-POC/blob/smbleed/demo.gif
SMBleedingGhost? The connection between SMBleed with SMBGhost for pre-auth RCE
Exploiting the SMBleed vulnerability without authentication is less straightforward, but still executable. ZecOps were able to combine it with the SMBGhost vulnerability for successful RCE (Remote Code Execution).
See POC demonstrating SMBGhost + SMBleed RCE POC Source Code
Source: https://github.com/ZecOps/CVE-2020-0796-RCE-POC/blob/master/demo.gif
Affected Versions:
Windows 10 versions 1903, 1909, 2004
Sources:
- https://blog.zecops.com/vulnerabilities/smbleedingghost-writeup-chaining-smbleed-cve-2020-1206-with-smbghost/
- https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-1206
- https://thehackernews.com/2020/06/SMBleed-smb-vulnerability.html
Appendix:
Below is a simplified version of the function:
ypedef struct _COMPRESSION_TRANSFORM_HEADER { ULONG ProtocolId; ULONG OriginalCompressedSegmentSize; USHORT CompressionAlgorithm; USHORT Flags; ULONG Offset; } COMPRESSION_TRANSFORM_HEADER, *PCOMPRESSION_TRANSFORM_HEADER; typedef struct _ALLOCATION_HEADER { // … PVOID UserBuffer; // … } ALLOCATION_HEADER, *PALLOCATION_HEADER; NTSTATUS Srv2DecompressData(PCOMPRESSION_TRANSFORM_HEADER Header, SIZE_T TotalSize) { PALLOCATION_HEADER Alloc = SrvNetAllocateBuffer( (ULONG)(Header->OriginalCompressedSegmentSize + Header->Offset), NULL); If (!Alloc) { return STATUS_INSUFFICIENT_RESOURCES; } ULONG FinalCompressedSize = 0; NTSTATUS Status = SmbCompressionDecompress( Header->CompressionAlgorithm, (PUCHAR)Header + sizeof(COMPRESSION_TRANSFORM_HEADER) + Header->Offset, (ULONG)(TotalSize – sizeof(COMPRESSION_TRANSFORM_HEADER) – Header->Offset), (PUCHAR)Alloc->UserBuffer + Header->Offset,
Header->OriginalCompressedSegmentSize, &FinalCompressedSize); if (Status < 0 || FinalCompressedSize != Header->OriginalCompressedSegmentSize) { SrvNetFreeBuffer(Alloc); return STATUS_BAD_DATA; } if (Header->Offset > 0) { memcpy( Alloc->UserBuffer, (PUCHAR)Header + sizeof(COMPRESSION_TRANSFORM_HEADER), Header->Offset); } Srv2ReplaceReceiveBuffer(some_session_handle, Alloc); return STATUS_SUCCESS; |