large bin attack

要求与使用

  • 需要有UAF
  • 可以写堆的内容
  • 最后的结果是可以修改任意地址的值为一个指针值,所以功能可能没那么大

Large chunk的特殊性

大于512(1024)字节的chunk称之为large chunk

被释放进Large Bin中的chunk,除prev_size、size、fd、bk之外,还具有fd_nextsizebk_nextsize:
fd_nextsize指向前一个与当前chunk大小不同的第一个空闲块,不包含bin的头指针
bk_nextsize指向后一个与当前chunk大小不同的第一个空闲块,不包含bin的头指针
一般空闲的large chunk在fd的遍历顺序中,按照由大到小的顺序排列。这样可以避免在寻找合适chunk时挨个遍历

Large Bin的插入顺序

在index相同的情况下:

  1. 按照大小,从大到小排序(小的链接large bin块)
  2. 如果大小相同,按照free的时间排序
  3. 多个大小相同的堆块,只有首堆块的fd_nextsize和bk_nextsize会指向其他堆块,后面的堆块的fd_nextsize和bk_nextsize均为0
  4. size最大的chunk的bk_nextsize指向最小的chunk,size最小的chunk的fd_nextsize指向最大的chunk

上文改自:https://blog.csdn.net/qq_41202237/article/details/112825556

实现

在how2heap中的例子:

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
 
int main()
{
    unsigned long stack_var1 = 0;
    unsigned long stack_var2 = 0;
    unsigned long *p1 = malloc(0x420);
    malloc(0x20);
    unsigned long *p2 = malloc(0x500);
     malloc(0x20);
    unsigned long *p3 = malloc(0x500);
    malloc(0x20);
 
    free(p1);
    free(p2);
    malloc(0x90);
    free(p3);
    
    p2[-1] = 0x3f1;
    p2[0] = 0;
    p2[2] = 0;
    p2[1] = (unsigned long)(&stack_var1 - 2);
    p2[3] = (unsigned long)(&stack_var2 - 4);
    
    malloc(0x90);
}

而文中还有一段指示到底是怎么作用的:

/*

    This technique is taken from
    https://dangokyo.me/2018/04/07/a-revisit-to-large-bin-in-glibc/

    [...]

              else
              {
                  victim->fd_nextsize = fwd;
                  victim->bk_nextsize = fwd->bk_nextsize;
                  fwd->bk_nextsize = victim;
                  victim->bk_nextsize->fd_nextsize = victim;
              }
              bck = fwd->bk;

    [...]

    mark_bin (av, victim_index);
    victim->bk = bck;
    victim->fd = fwd;
    fwd->bk = victim;
    bck->fd = victim;

    For more details on how large-bins are handled and sorted by ptmalloc,
    please check the Background section in the aforementioned link.

    [...]

 */

解释

  1. 创建三个large chunk并且用小的隔开,以防止合并
  2. p1,p2释放,这时两者都会进入unsorted bin
  3. 创建一个较小块,会给p1切一块下来,而p2会被放到large bin中
  4. 然后再释放p3,此时p3会待在unsorted bin中
  5. 而后对largebin中的块进行赋值,更改size,bk,bk_nextsize三个值。需要减小第二个freed chunk的大小以让第三个freed chunk可以被放到它的前面,为了覆盖堆栈,将bk设置为stack_var1之前16个bytes,将bk_nextsize设置为stack_var2之前4个bytes。
  6. 此时再创建一个0x90大小的块,这时会继续切p1,而后p3进入large bin,出现问题:

    • else
                    {
                        victim->fd_nextsize = fwd;
                        victim->bk_nextsize = fwd->bk_nextsize;
                        fwd->bk_nextsize = victim;
                        victim->bk_nextsize->fd_nextsize = victim;
                    }
                    bck = fwd->bk;
      
    • 上面的代码中,victimp3fwdp2
    • 可以看到,p3->bk_nextsize=p2->bk_nextsize,并且p3->bk_nextsize->fd_nextsize=p3,即p2->bk_nextsize+4=stack_var2=p3
    • 而又有代码:
    • bck = fwd->bk;
      victim->bk = bck;
          victim->fd = fwd;
          fwd->bk = victim;
          bck->fd = victim;
    • 可以看到,p2->bk->fd=p3,即p2->bk+2=stack_var1=p3
    • 有个注意点,创建堆时产生的指针是指向空白位置的,而bin中得到指针是指向大小的。
  7. 从而使得两个值都变成了p3指针的值