# SMBleedingGhost Writeup: Chaining SMBleed (CVE-2020-1206) with SMBGhost
## SHARE THIS ARTICLE
## TL;DR
- While looking at the vulnerable function of SMBGhost, we discovered another vulnerability: SMBleed (CVE-2020-1206).
- SMBleed allows to leak kernel memory remotely.
- Combined with SMBGhost, which was patched three months ago, SMBleed allows to achieve pre-auth Remote Code Execution (RCE).
- POC #1: SMBleed remote kernel memory read: [POC #1 Link](https://github.com/ZecOps/CVE-2020-1206-POC)
- POC #2: Pre-Auth RCE Combining SMBleed with SMBGhost: [POC #2 Link](https://github.com/ZecOps/CVE-2020-0796-RCE-POC)
## Introduction
The SMBGhost (CVE-2020-0796) bug in the compression mechanism of SMBv3.1.1 was
[fixed about three months ago](https://portal.msrc.microsoft.com/en-
US/security-guidance/advisory/adv200005). In our [previous
writeup](https://blog.zecops.com/vulnerabilities/exploiting-smbghost-
cve-2020-0796-for-a-local-privilege-escalation-writeup-and-poc/) we explained
the bug, and demonstrated a way to exploit it for local privilege escalation.
As we found during our research, it's not the only bug in the SMB
decompression functionality. SMBleed happens in the same function as SMBGhost.
The bug allows an attacker to read uninitialized kernel memory, as we
illustrated in detail in this writeup.
## An observation
The bug happens in the same function as with SMBGhost, the Srv2DecompressData
function in the srv2.sys SMB server driver. Below is a simplified version of
the function, with the irrelevant details omitted:
```
typedef 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;
}
```
The Srv2DecompressData function receives the compressed message which is sent
by the client, allocates the required amount of memory, and decompresses the
data. Then, if the Offset field is not zero, it copies the data that is placed
before the compressed data as is to the beginning of the allocated buffer.
![](https://images.seebug.org/1591769290939-w331s)
The SMBGhost bug happened due to lack of integer overflow checks. It was fixed
by Microsoft and even though we didn't add it to our function to keep it
simple, this time we will assume that the function checks for integer
overflows and discards the message in these cases. Even with these checks in
place, there's still a serious bug. Can you spot it?
## Faking OriginalCompressedSegmentSize again
Previously, we exploited SMBGhost by setting the OriginalCompressedSegmentSize
field to be a huge number, causing an integer overflow followed by an out of
bounds write. What if we set it to be a number which is just a little bit
larger than the actual decompressed data we send? For example, if the size of
our compressed data is x after decompression, and we set
OriginalCompressedSegmentSize to be x + 0x1000, we'll get the following:
![](https://images.seebug.org/1591769292304-w331s)
The uninitialized kernel data is going to be treated as a part of our message.
If you didn't read our previous writeup, you might think that the
Srv2DecompressData function call should fail due to the check that follows the
SmbCompressionDecompress call:
```
if (Status < 0 || FinalCompressedSize != Header->OriginalCompressedSegmentSize) {
SrvNetFreeBuffer(Alloc);
return STATUS_BAD_DATA;
}
```
Specifically, in our example, you might assume that while the value of the
OriginalCompressedSegmentSize field is x + 0x1000, FinalCompressedSize will be
set to x in this case. In fact, FinalCompressedSize will be set to x + 0x1000
as well due to the implementation of the SmbCompressionDecompress function:
```
NTSTATUS SmbCompressionDecompress(
USHORT CompressionAlgorithm,
PUCHAR UncompressedBuffer,
ULONG UncompressedBufferSize,
PUCHAR CompressedBuffer,
ULONG CompressedBufferSize,
PULONG FinalCompressedSize)
{
// ...
NTSTATUS Status = RtlDecompressBufferEx2(
...,
FinalUncompressedSize,
...);
if (status >= 0) {
*FinalCompressedSize = CompressedBufferSize;
}
// ...
return Status;
}
```
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
The SMB message we used to demonstrate the vulnerability is the [SMB2 WRITE
message](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-
smb2/e7046961-3318-4350-be2a-a8d69bb59ce8). The message structure contains
fields such as the amount of bytes to write and flags, followed by a variable
length buffer. 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. We based our POC on Microsoft's
WindowsProtocolTestSuites repository (that we also used for [the first
SMBGhost reproduction](https://blog.zecops.com/vulnerabilities/vulnerability-
reproduction-cve-2020-0796-poc/)), introducing this small addition to the
compression function:
```
// HACK: fake size
if (((Smb2SinglePacket)packet).Header.Command == Smb2Command.WRITE)
{
((Smb2WriteRequestPacket)packet).PayLoad.Length += 0x1000;
compressedPacket.Header.OriginalCompressedSegmentSize += 0x1000;
}
```
Note that our 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 we
control the allocation size, we might be able to control the data that is
being leaked to some degree.
[SMBleed POC Source Code](https://github.com/ZecOps/CVE-2020-1206-POC)
![](https://images.seebug.org/1591769293395-w331s)
## Affected Windows versions
Windows 10 versions 1903, 1909 and 2004 are affected. During testing, our POC
crashed one of our Windows 10 1903 machines. After analyzing the crash with
[**Neutrino**](https://www.zecops.com/solutions/neutrino.html) **** we saw
that the earliest, unpatched versions of Windows 10 1903 have a null pointer
dereference bug while handling valid, compressed SMB packets. Please note, we
didn't investigate further to find whether it's possible to bypass the null
pointer dereference bug and exploit the system.
![](https://images.seebug.org/1591769297372-w331s)_An unpatched system, null
pointer dereference happens here._
![](https://images.seebug.org/1591769300048-w331s)_A patched system, the added
null pointer check._
Here's a summary of the affected Windows versions with the relevant updates
installed:
**Windows 10 Version 2004**
**Update**| **SMBGhost**| **SMBleed**
---|---|---
KB4557957| Not Vulnerable| Not Vulnerable
Before KB4557957| Not Vulnerable| Vulnerable
**Windows 10 Version 1909**
**Update**| **SMBGhost**| **SMBleed**
---|---|---
KB4560960| Not Vulnerable| Not Vulnerable
KB4551762| Not Vulnerable| Vulnerable
Before KB4551762| Vulnerable| Vulnerable
**Windows 10 Version 1903**
**Update**| **Null Dereference Bug**| **SMBGhost**| **SMBleed**
---|---|---|---
KB4560960| Fixed| Not Vulnerable| Not Vulnerable
KB4551762| Fixed| Not Vulnerable| Vulnerable
KB4512941| Fixed| Vulnerable| Vulnerable
None of the above| Not Fixed| Vulnerable| Potentially vulnerable*
- We haven't tried to bypass the null dereference bug, but it may be possible through another method (for example, using SMBGhost Write-What-Where primitive)
## SMBleedingGhost? Chaining SMBleed with SMBGhost for pre-auth RCE
Exploiting the SMBleed bug without authentication is less straightforward, but
also possible. We were able to use it together with the SMBGhost bug to
[achieve RCE (Remote Code
Execution)](https://twitter.com/ZecOps/status/1252288104435761154). A writeup
with the technical details will be published soon. For now, please see below a
POC demonstrating the exploitation. This POC is released only for educational
and research purposes, as well as for evaluation of security defenses. Use at
your own risk. ZecOps takes no responsibility for any misuse of this POC.
[](https://github.com/ZecOps/CVE-2020-0796-RCE-POC)[SMBGhost + SMBleed RCE POC
Source Code](https://github.com/ZecOps/CVE-2020-0796-RCE-POC)
![](https://images.seebug.org/1591769302410-w331s)
![](https://images.seebug.org/1591769319586-w331s)
暂无评论