Seeing (Mail)Demons? Technique and Triggers

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.

May 9 2020 by

Jamf Threat Labs

Treasure chest open with gold light shining from within

Impact & Key Details (TL;DR)

  1. Demonstrate a way to do a basic heap spray.
  2. We were able to use this technique to verify that this vulnerability is exploitable. We are still working on improving the success rate.
  3. 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.
  4. Suggestions to Apple on improving forensics information/logs and important questions following Apple’s response to the previous disclosure.
  5. 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.
Infographic on vulnerability lifecycle

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
  • 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.

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 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:

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:]

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.

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 is 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 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 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.

Case 2

Following is another crash that happened after an email was received and processed.

[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.


  1. It is rare to see that user-provided inputs trigger and control remote vulnerabilities.
  2. We prove that it is possible to exploit this vulnerability using the described technique.
  3. We have observed real-world triggers with a large allocation size.
  4. We have seen real-world triggers with values that are controlled by the sender.
  5. The emails we looked for were missing/deleted.
  6. The success rate can be improved. This bug had in-the-wild triggers in 2010 on an iPhone 2G device.
  7. 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:

  1. Crashes improvement: Enable to see memory next to each pointer/register.
  2. Crashes improvement: Show stack/heap memory/memory near registers.
  3. Add PIDs/PPIDs/UID/EUID to all applicable events.
  4. 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.
  5. 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.

Subscribe to the Jamf Blog

Have market trends, Apple updates and Jamf news delivered directly to your inbox.

To learn more about how we collect, use, disclose, transfer, and store your information, please visit our Privacy Policy.