largebin attack 由这个名字就可以看出是对 largebin 进行的操作,需要的条件是存在 UAF 或者可以构造出 UAF。实现的功能是:
1、任意地址写入一个大数字
2、实现任意地址分配(这个好像也叫 house of strom,以后另开一章来记)
关键源码:
else { victim_index = largebin_index (size); bck = bin_at (av, victim_index); fwd = bck->fd; /* maintain large bins in sorted order */ if (fwd != bck) { /* Or with inuse bit to speed comparisons */ size |= PREV_INUSE; /* if smaller than smallest, bypass loop below */ assert ((bck->bk->size & NON_MAIN_ARENA) == 0); if ((unsigned long) (size) < (unsigned long) (bck->bk->size)) { fwd = bck; bck = bck->bk; victim->fd_nextsize = fwd->fd; victim->bk_nextsize = fwd->fd->bk_nextsize; fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim; } else { assert ((fwd->size & NON_MAIN_ARENA) == 0); while ((unsigned long) size < fwd->size) { fwd = fwd->fd_nextsize; assert ((fwd->size & NON_MAIN_ARENA) == 0); } if ((unsigned long) size == (unsigned long) fwd->size) /* Always insert in the second position. */ fwd = fwd->fd; else { victim->fd_nextsize = fwd; victim->bk_nextsize = fwd->bk_nextsize; fwd->bk_nextsize = victim; victim->bk_nextsize->fd_nextsize = victim; } bck = fwd->bk; } } else victim->fd_nextsize = victim->bk_nextsize = victim; } mark_bin (av, victim_index); victim->bk = bck; victim->fd = fwd; fwd->bk = victim; bck->fd = victim;
我们用 hollk 师傅改过的 how2heap 的源码来当作示例。(源码会放在附件里)
首先定义了两个无符号长整型并且赋值为0。
可以看到 stack_var1_addr = 0x7fffffffdfc8 , stack_var2_addr = 0x7fffffffdfd0 , 并且只都为 0。
再往下继续走,到创建完最后一个 0x30 大小的堆块。
p1,p2,p3 分别对应着0x330,0x410,0x410 大小的堆块头指针。
接下来去释放p1
再去释放p2
从wiki上抠出一张图
由于p1,p2都超过了 fastbin 的最大大小他们都会进入 unsorted bin,再往下,由于 chunk1 < 0x400,最后会进 small bin,而 chunk2 > 0x400,故最后会进large bin。
再往下到void* p4 = malloc(0x90); 申请了一个 0xa0 的堆块。这一行其实做了很多的事情。
1、通过 bk 从unsorted bin 拿出 chunk1 放入small bin ,并标记这个 small bin 中有空闲的 chunk。
2、继续通过 bk 从 unsorted bin 拿出 chunk2 放入 large bin,并标记这个large bin 有空闲的 chunk。
3、从small bin 中分割出一小块来满足 p4 ,再把剩下的 chunk(0x330 - 0xa0),放回unsorted bin 中。
我们接下来再释放一个 0x410 大小的 chunk,也就是 p3
由于他的大小是 0x410,所以也是会先被放进 unsorted bin过度,再放进 large bin中。
接下来的几行就是我们对 p2 直接进行的构造
构造前:
构造后:
我们把
1、size 由 0x411 改为了 0x3f1
2、fd 和 fd_nextsize 置零
3、bk 改为 stack_var1_addr - 0x10(0x00007fffffffdfb8)
4、bk_nextsize 改为 stack_var2_addr - 0x20(0x00007fffffffdfb0)
下面又从 hollk 师傅的文章里抠出一张图
再往下继续void* p4 = malloc(0x90); 申请了一个 0xa0 的堆块。干的事情也很多
1、通过 bk 从unsorted bin 拿出 刚才剩下的那个 chunk(0x290) 放入small bin ,并标记这个 small bin 中有空闲的 chunk。
2、继续通过 bk 从 unsorted bin 拿出 chunk3 放入 large bin,并标记这个large bin 有空闲的 chunk。
3、从 small bin 中分割出一小块来满足 p4 ,再把剩下的 chunk(0x290 - 0xa0),放回unsorted bin 中。
由于 chunk3 是属于 large bin 的 chunk ,故会进入 large bin 的分支,我们再来看上面的源码。
里面的 fwd 就是 p2,victim 也就是 p3,bck 也就是 p2->bk。
由于 p2 的 size 被我们改过了 0x3f1,比 p3 的要小,故前面两个 if 语句不符合,最后会进入如下内容
else { victim->fd_nextsize = fwd; victim->bk_nextsize = fwd->bk_nextsize; fwd->bk_nextsize = victim; victim->bk_nextsize->fd_nextsize = victim; }
我们之前的构造已经使 p2->bk->fd = stack_var1_addr , p2->bk_nextsize->fd_nextsize = stack_var2_addr。
经过这四行的源码就会使
1、p3->fd_nextsize = p2
2、p3->bk_nextsize = p2->bk_nextsize
3、p2->bk_nextsize = p3
4、p3->bk_nextsize->fd_nextsize = p3
2+4组合起来也就是 p3->bk_nextsize = p2->bk_nextsize = stack_var2_addr - 0x20,p3->bk_nextsize->fd_nextsize =*(( stack_var2_addr - 0x20 )+ 0x20) = stack_var2 = p3
也就是 stack_var2 = p3。
接着源码里也有这几行
mark_bin (av, victim_index); victim->bk = bck; victim->fd = fwd; fwd->bk = victim; bck->fd = victim;
那么就会使
1、p3->bk = p2->bk
2、p3->fd = p2
3、p2->bk = p3
4、p2->bk->fd = p3
构造好的数据结合4就可以使 p2->bk->fd = *((stack_var2_addr - 0x10)+ 0x10)= stack_var2 = p3。
看一下最后修改的结果,也就是把 stack_var1 和 stack_var2 里的值改为 p3 的头指针。
最后补充一下 large bin attack 需要的条件
1、可以修改某个 large bin 的数据 size , fd , bk , fd_nextsize , bk_nextsize
2、构造过的 large bin chunk 要在正常 large bin chunk 的前面进入 large bin链表
附件
提取码:fs74
参考链接:
https://xz.aliyun.com/t/5177?page=1
https://ctf-wiki.org/pwn/linux/user-mode/heap/ptmalloc2/large-bin-attack/
https://blog.csdn.net/qq_41202237/article/details/112825556