Jamf Threat Labs team researchers provide a deep dive into triggers that have been found in the wild, relating to the MailDemon vulnerability. Also, techniques on how to uncover this critical security threat.
Impact & Key Details (TL;DR)
Demonstrate a way to do a basic heap spray.
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 are worth an out-of-band patch.
Suggestions to Apple on improving forensics information/logs and important questions following Apple’s response to the previous disclosure.
MailDemon appears to be even older 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 discovery announcement of RCE vulnerabilities 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.
Jamf encourages Apple to release an out-of-band patch for the recently disclosed vulnerabilities and hopes this blog will provide additional reinforcement to release patches as early as possible. In this blog post, 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 the 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, we now 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 low probability exploitation (LPE). This 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.
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 to 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.
Another way is using MFMutableData itself – when the size 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 the content of the email into lines less than 0x20000 bytes since the IMAP library reads email content by lines. With this primitive, we can place the payload into our desired address.
An oversized email is capable of reproducing the vulnerability as a POC, but for a stable exploit, we need to take a closer look at -[MFMutableData appendBytes:length:]
int old_len = [self length];
char* bytes = self->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 an invalid address which is beyond the edge of this region and crash immediately, given that the size of the 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.
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 is completely controlled by the sender.
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 in the 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.
Note: that x8-x15, and x0 are fully controlled by the sender.
The crash gives us another answer to our question above: What if we accumulate too much data before triggering the vulnerability? The allocation of the 8 bytes of 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!
Also of note, we found a public example of a similar trigger by an anonymous user in the modmy.com forums:
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 first 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 the 8-byte memory failure, however, it is not possible to determine the exact reason since the log lacked information on memory regions. 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 of 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 look at the virtual memory region information of x28: 0x0000000282693330. The overwritten object was stored in the MALLOC_NANO region, which stores small heap chunks. The heap overflow vulnerability corrupts the same region since it overflows on an 8-byte heap chunk which is also stored in MALLOC_NANO.
This crash is quite 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 a fake method cache, the attackers could control the PC pointer. This phrack blog explains the details.
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.
The 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 list of suggested improvements above)
Jamf protects against this and numerous other security threats impacting your enterprise fleet.
Jamf Threat Labs is a global team of experienced threat researchers, cybersecurity experts and data scientists with skills that span penetration testing, network monitoring, malware research and app risk assessment. Jamf Threat Labs primarily monitors and explores emerging threats affecting Mac and mobile devices. The team’s research is published with the aim of raising awareness of specific threats while also improving awareness and advocacy of security practices to protect the modern workforce.