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
POC #2: Pre-Auth RCE Combining SMBleed with SMBGhost: POC #2 Link
Introduction
The SMBGhost (CVE-2020-0796) bug in the compression mechanism of SMBv3.1.1 was fixed about three months ago. In our previous writeup 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.
Hear the news first
Only essential content
New vulnerabilities & announcements
News from ZecOps Research Team
Your subscription request to ZecOps Blog has been successfully sent.
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:
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.
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:
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:
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. 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), introducing this small addition to the compression function:
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.
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 Neutrinowe 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.
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). 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.
ZecOps Neutrino customers detect exploitation of SMBleed & SMBGhost – no further action is required. SMBleed & SMBGhost can be detected in multiple ways, including crash dump analysis, a network traffic analysis. Signatures are available to ZecOps Threat Intelligence subscribers. Feel free to reach out to us at [email protected] for more information.
Remediation
You can remediate both SMBleed and SMBGhost by doing one or more of the following things:
Windows update will solve the issues completely (recommended)
Blocking port 445 will stop lateral movements using these vulnerabilities
Enforcing host isolation
Disabling SMB 3.1.1 compression (not a recommended solution)
Shout out to Chompie that exploited this bug with a different technique. Chompie’s POC is available here.
Further to Apple’s patch of the MailDemon vulnerability (see our blog here), ZecOps Research Team has analyzed and compared the MailDemon patches of iOS 13.4.5 beta and iOS 13.5.
Our analysis concluded that the patches are different, and that iOS 13.4.5 beta patch was incomplete and could be still vulnerable under certain circumstances.
Since the 13.4.5 beta patch was insufficient, Apple issued a complete patch utilising a different approach which fixed this issue completely on both iOS 13.5 and iOS 12.4.7 as a special security update for older devices.
This may explain why it took about one month for a full patch to be released.
iOS 13.4.5 beta patch
The following is the heap-overflow vulnerability patch on iOS 13.4.5 beta.
The function -[MFMutableData appendBytes:length:] raises an exception if -[MFMutableData _mapMutableData] returns false.
In order to see when -[MFMutableData _mapMutableData] returns false, let’s take a look at how it is implemented:
When mmap fails it returns False, but still allocates a 8-bytes chunk and stores the pointer in self->bytes. This patch raises an exception before copying data into self->bytes, which solves the heap overflow issue partially.
The patch makes sure an exception will be raised inside -[MFMutableData appendBytes:length:]. However, there are other functions that call -[MFMutableData _mapMutableData] and interact with self->bytes which will be an 8-bytes chunk if mmap fails, these functions do not check if mmap fails or not since the patch only affects -[MFMutableData appendBytes:length:].
Following is an actual backtrace taken from MobileMail:
Since the bytes returned by mutableBytes is usually considered to be modifiable given following from Apple’s documentation:
This property is similar to, but different than the bytes property. The bytes property contains a pointer to a constant. You can use The bytes pointer to read the data managed by the data object, but you cannot modify that data. However, if the mutableBytes property contains a non-null pointer, this pointer points to mutable data. You can use the mutableBytes pointer to modify the data managed by the data object.
Apple’s documentation
Both -[MFMutableData mutableBytes] and -[MFMutableData bytes] returns self->bytes points to the 8-bytes chunk if mmap fails, which might lead to heap overflow under some circumstances.
The following is an example of how things could go wrong, the heap overflow still would happen even if it checks length before memcpy:
size_t length = 0x30000;
MFMutableData* mdata = [MFMutableData alloc];
data = malloc(length);
[mdata initWithBytesNoCopy:data length:length];
size_t mdata_len = [mdata length];
char* mbytes = [mdata mutableBytes];//mbytes could be a 8-bytes chunk
size_t new_data_len = 90;
char* new_data = malloc(new_data_len);
if (new_data_len <= mdata_len) {
memcpy(mbytes, new_data, new_data_len);//heap overflow if mmap fails
}
iOS 13.5 Patch
Following the iOS 13.5 patch, an exception is raised in “-[MFMutableData _mapMutableData] ”, right after mmap fails and it doesn’t return the 8-bytes chunk anymore. This approach fixes the issue completely.
Summary
iOS 13.5 patch is the correct way to patch the heap overflow vulnerability. It is important to double check security patches and verify that the patch is complete.
At ZecOps we help developers to find security weaknesses, and validate if the issue was correctly solved automatically. If you would like to find similar vulnerabilities in your applications/programs, we are now adding additional users to our CrashOps SDK beta program. If you do not own an app, and would like to inspect your phone for suspicious activity – check out ZecOps iOS DFIR solution – Gluon.
We were able to use this technique to verify that this vulnerability is exploitable. We are still working on improving the success rate.
Present two new examples of in-the-wild triggers so you can judge by yourself if these bugs worth an out of band patch
Suggestions to Apple on how to improve forensics information / logs and important questions following Apple’s response to the previous disclosure
Launching a bounty program for people who have traces of attacks with total bounties of $27,337
MailDemon appears to be even more ancient than we initially thought. There is a trigger for this vulnerability, in the wild, 10 years ago, on iPhone 2g, iOS 3.1.3
Following our announcement of RCE vulnerabilities discovery in the default Mail application on iOS, we have been contacted by numerous individuals who suspect they were targeted by this and related vulnerabilities in Mail.
ZecOps encourages Apple to release an out of band patch for the recently disclosed vulnerabilities and hopes that this blog will provide additional reinforcement to release patches as early as possible. In this blogpost we will show a simple way to spray the heap, whereby we were able to prove that remote exploitation of this issue is possible, and we will also provide two examples of triggers observed in the wild.
At present, we already have the following:
Remote heap-overflow in Mail application
Ability to trigger the vulnerability remotely with attacker-controlled input through an incoming mail
Ability to alter code execution
Kernel Elevation of Privileges 0day
What we don’t have:
An infoleak – but therein rests a surprise: an infoleak is not mandatory to be in Mail since an infoleak in almost any other process would be sufficient. Since dyld_shared_cache is shared through most processes, an infoleak vulnerability doesn’t necessarily have to be inside MobileMail, for example CVE-2019-8646 of iMessage can do the trick remotely as well – which opens additional attack surface (Facetime, other apps, iMessage, etc). There is a great talk by 5aelo during OffensiveCon covering similar topics.
Therefore, now we have all the requirements to exploit this bug remotely. Nonetheless, we prefer to be cautious in chaining this together because:
We have no intention of disclosing the LPE – it allows us to perform filesystem extraction / memory inspection on A12 devices and above when needed. You can read more about the problems of analyzing mobile devices at FreeTheSandbox.org
We haven’t seen exploitation in the wild for the LPE.
We will also share two examples of triggers that we have seen in the wild and let you make your own inferences and conclusions.
were you targeted by this vulnerability?
MailDemon Bounty
Lastly, we will present a bounty for those submissions that were able to demonstrate that they were attacked.
Exploiting MailDemon
As we previously hinted, MailDemon is a great candidate for exploitation because it overwrites small chunks of a MALLOC_NANO memory region, which stores a large number of Objective-C objects. Consequently, it allows attackers to manipulate an ISA pointer of the corrupted objects (allowing them to cause type confusions) or overwrite a function pointer to control the code flow of the process. This represents a viable approach of taking over the affected process.
Heap Spray & Heap Grooming Technique
In order to control the code flow, a heap spray is required to place crafted data into the memory. With the sprayed fake class containing a fake method cache of ‘dealloc’ method, we were able to control the Program Counter (PC) register after triggering the vulnerability using this method*.
The following is a partial crash log generated while testing our POC:
Exception Type: EXC_BAD_ACCESS (SIGBUS)
Exception Subtype: EXC_ARM_DA_ALIGN at 0xdeadbeefdeadbeef
VM Region Info: 0xdeadbeefdeadbeef is not in any region. Bytes after previous region: 16045690973559045872
REGION TYPE START - END [ VSIZE] PRT/MAX SHRMOD REGION DETAIL
MALLOC_NANO 0000000280000000-00000002a0000000 [512.0M] rw-/rwx SM=PRV
--->
UNUSED SPACE AT END
Thread 18 name: Dispatch queue: com.apple.CFNetwork.Connection
Thread 18 Crashed:
0 ??? 0xdeadbeefdeadbeef 0 + -2401053088876216593
1 libdispatch.dylib 0x00000001b7732338 _dispatch_lane_serial_drain$VARIANT$mp + 612
2 libdispatch.dylib 0x00000001b7732e74 _dispatch_lane_invoke$VARIANT$mp + 480
3 libdispatch.dylib 0x00000001b773410c _dispatch_workloop_invoke$VARIANT$mp + 1960
4 libdispatch.dylib 0x00000001b773b4ac _dispatch_workloop_worker_thread + 596
5 libsystem_pthread.dylib 0x00000001b796a114 _pthread_wqthread + 304
6 libsystem_pthread.dylib 0x00000001b796ccd4 start_wqthread + 4
Thread 18 crashed with ARM Thread State (64-bit):
x0: 0x0000000281606300 x1: 0x00000001e4b97b04 x2: 0x0000000000000004 x3: 0x00000001b791df30
x4: 0x00000002827e81c0 x5: 0x0000000000000000 x6: 0x0000000106e5af60 x7: 0x0000000000000940
x8: 0x00000001f14a6f68 x9: 0x00000001e4b97b04 x10: 0x0000000110000ae0 x11: 0x000000130000001f
x12: 0x0000000110000b10 x13: 0x000001a1f14b0141 x14: 0x00000000ef02b800 x15: 0x0000000000000057
x16: 0x00000001f14b0140 x17: 0xdeadbeefdeadbeef x18: 0x0000000000000000 x19: 0x0000000108e68038
x20: 0x0000000108e68000 x21: 0x0000000108e68000 x22: 0x000000016ff3f0e0 x23: 0xa3a3a3a3a3a3a3a3
x24: 0x0000000282721140 x25: 0x0000000108e68038 x26: 0x000000016ff3eac0 x27: 0x00000002827e8e80
x28: 0x000000016ff3f0e0 fp: 0x000000016ff3e870 lr: 0x00000001b6f3db9c
sp: 0x000000016ff3e400 pc: 0xdeadbeefdeadbeef cpsr: 0x60000000
The ideal primitive for heap spray in this case is a memory leak bug that can be triggered from remote, since we want the sprayed memory to stay untouched until the memory corruption is triggered. We left this as an exercise for the reader. Such primitive could qualify for up to $7,337 bounty from ZecOps (read more below).
Another way is using MFMutableData itself – when the size of MFMutableData is less than 0x20000 bytes it allocates memory from the heap instead of creating a file to store the content. And we can control the MFMutableData size by splitting content of the email into lines less than 0x20000 bytes since the IMAP library reads email content by lines. With this primitive we have a better chance to place payload into the address we want.
Trigger
An oversized email is capable of reproducing the vulnerability as a PoC(see details in our previous blog), but for a stable exploit, we need to take a closer look at “-[MFMutableData appendBytes:length:]“
-[MFMutableData appendBytes:length:]
{
int old_len = [self length];
//...
char* bytes = self->bytes;
if(!bytes){
bytes = [self _mapMutableData]; //Might be a data pointer of a size 8 heap
}
copy_dst = bytes + old_len;
//...
memmove(copy_dst, append_bytes, append_length); // It used append_length to copy the memory, causing an OOB writing in a small heap
}
The destination address of memove is ”bytes + old_len” instead of’ ‘bytes”. So what if we accumulate too much data before triggering the vulnerability? The “old_len” would end up with a very big value so that the destination address will end up in a invalid address which is beyond the edge of this region and crash immediately, given that the size of MALLOC_NANO region is 512MB.
In order to reduce the size of “padding”, we need to consume as much data as possible before triggering the vulnerability – a memory leak would be one of our candidates.
Noteworthy, the “padding” doesn’t mean the overflow address is completely random, the “padding” is predictable by hardware models since the RAM size is the same, and mmap is usually failed at the same size during our tests.
Crash analysis
This post discusses several triggers and exploitability of the MobileMail vulnerability detected in the wild which we covered in our previous blog.
Case 1 shows that the vulnerability is triggered in the wild before it was disclosed.
Case 2 is due to memory corruption in the MALLOC_NANO region, the value of the corrupted memory is part of the sent email and completely controlled by the sender.
Case 1
The following crash was triggered right inside the vulnerable function while the overflow happens.
With [a] and [b] we know that the process crashed inside “memmove” called by “-[MFMutableData appendBytes:length:]”, which means the value of “copy_dst” is an invalid address at first place which is 0x4a35630e.
So where did the value of the register x0 (0x4a35630e) come from? It’s much smaller than the lowest valid address.
Turns out that the process crashed when after failing to mmap a file and then failing to allocate the 8 byte memory at the same time.
The invalid address 0x4a35630e is actually the offset which is the length of MFMutableData before triggering the vulnerability(i.e. “old_len”). When calloc fails to allocate the memory it returns NULL, so the copy_dst will be “0 + old_len(0x4a35630e)”.
In this case the “old_len” is about 1.2GB which matches the average length of our POC which is likely to cause mmap failure and trigger the vulnerability.
Please note that x8-x15, and x0 are fully controlled by the sender.
The crash gives us another answer for our question above: “What if we accumulate too much data before triggering the vulnerability?” – The allocation of the 8-bytes memory could fail and crash while copying the payload to an invalid address. This can make reliable exploitation more difficult, as we may crash before taking over the program counter.
A Blast From The Past: Mysterious Trigger on iOS 3.1.3 in 2010!
Vulnerable version: iOS 3.1.3 on iPhone 2G Time of crash: 22nd of October, 2010
The user “shyamsandeep”, registered on the 12th of June 2008 and last logged in on the 16th of October 2011 and had a single post in the forum, which contained this exact trigger.
This crash had r0 equal to 0x037ea000, which could be the result of the 1st vulnerability we disclosed in our previous blog which was due to ftruncate() failure. Interestingly, as we explained in the first case, it could also be a result of the allocation of 8-bytes memory failure however it is not possible to determine the exact reason since the log lacked memory regions information. Nonetheless, it is certain that there were triggers in the wild for this exploitable vulnerability since 2010.
[a]: The pointer of the object was overwritten with “0x0041004100410041” which is AAAA in unicode.
[b] is one of the instructions around the crashed address we’ve added for better understanding, the process crashed on instruction “ldr x8, [x0]” while -[__NSDictionaryM removeAllObjects] was trying to release one the objects.
By reverse engineering -[__NSDictionaryM removeAllObjects], we understand that register x0 was loaded from x28(0x0000000282693330), since register x28 was never changed before the crash.
Let’s take a look at the virtual memory region information of x28: 0x0000000282693330, the overwritten object was stored in MALLOC_NANO region which stores small heap chunks. The heap overflow vulnerability corrupts the same region since it overflows on a 8-bytes heap chunk which is also stored in MALLOC_NANO.
This crash is actually pretty close to controlling the PC since it controls the pointer of an Objective-C object. By pointing the value of register x0 to a memory sprayed with a fake object and class with fake method cache, the attackers could control the PC pointer, this phrack blog explains the details.
Summary
It is rare to see that user-provided inputs trigger and control remote vulnerabilities.
We prove that it is possible to exploit this vulnerability using the described technique.
We have observed real world triggers with a large allocation size.
We have seen real world triggers with values that are controlled by the sender.
The emails we looked for were missing / deleted.
Success-rate can be improved. This bug had in-the-wild triggers in 2010 on an iPhone 2G device.
In our opinion, based on the above, this bug is worth an out of band patch.
How Can Apple Improve the Logs?
The lack of details in iOS logs and the lack of options to choose the granularity of the data for both individuals and organizations need to change to get iOS to be on-par with MacOS, Linux, and Windows capabilities. In general, the concept of hacking into a phone in order to analyze it, is completely flawed and should not be the normal way to do it.
We suggest Apple improve its error diagnostics process to help individuals, organizations, and SOCs to investigate their devices. We have a few helpful technical suggestions:
Crashes improvement: Enable to see memory next to each pointer / register
Crashes improvement: Show stack / heap memory / memory near registers
Add PIDs/PPIDs/UID/EUID to all applicable events
Ability to send these logs to a remote server without physically connecting the phone – we are aware of multiple cases where the logs were mysteriously deleted
Ability to perform complete digital forensics analysis of suspected iOS devices without a need to hack into the device first.
Questions for Apple
How many triggers have you seen to this heap overflow since iOS 3.1.3?
How were you able to determine within one day that all of the triggers to this bug were not malicious and did you actually go over each event ?
When are you planning to patch this vulnerability?
What are you going to do about enhancing forensics on mobile devices (see the list above)?
MailDemon Bounty
If you experienced any of the three symptoms below, use another mail application (e.g. Outlook for Desktop), and send the relevant emails (including the Email Source) to the address [email protected]– there are instructions at the bottom of this post.
Suspected emails may appear as follows:
Bounty details: We will validate if the email contains an exploit code. For the first two submissions containing Mail exploits that were verified by ZecOps team, we will provide:
$10,000 USD bounty
One license for ZecOps Gluon (DFIR for mobile devices) for 1 year
One license for ZecOps Neutrino (DFIR for endpoints and servers) for 1 year.
We will provide an additional bounty of up to $7,337 for exploit primitive as described above.
We will determine what were the first two valid submissions according to the date they were received in our email server and if they contain an exploit code. A total of $27,337 USD in bounties and licenses of ZecOps Gluon & Neutrino.
For suspicious submissions, we would also request device logs in order to determine other relevant information about potential attackers exploiting vulnerabilities in Mail and other vulnerabilities on the device.
Please note: Not every email that causes the symptoms above and shared with us will qualify for a bounty as there could be other bugs in MobileMail/maild – we’re only looking for ones that contain an attack.
How to send the emails using Outlook :
Open Outlook from a computer and locate the relevant email