首页
社区
课程
招聘
[原创]unsortbin attack分析与总结
发表于: 2020-10-3 11:06 6479

[原创]unsortbin attack分析与总结

2020-10-3 11:06
6479

想要弄明白largebin的相关内容,首先要整理清楚unsortbin的分配利用方式。

可以看到,首先插入了0x602000(ptr1)然后在头部插入了0x6020f0(ptr3),看一下fd和bk指针如下:

ptr1的bk指向ptr3,ptr3的bk指向ptr1,并且是个循环链表,ptr1的fd指向unsortbin,ptr3的bk指向unsortbin。

然后我觉得有一句形如fd和bk很清楚:

fd pointer to the next free chunk; bk pointer to the pre free chunk

这里的pre和next是相对unsorbtin里的位置说的,比如说先free 1,先插入1,再free2,再插入2,那么unsortbin中的就是unsortbin->2->1。那么2->fd=&1;2->bk=unsorbtin | 1->fd=unsortbin;1->bk=&2

然后往出取的时候从链尾取(注意:当malloc时程序首先查找fastbin和smallbin。如果在 fastbin,small bin 中找不到对应大小的 chunk,就会尝试从 Unsorted Bin 中寻找 chunk。如果取出来的 chunk 大小刚好满足,就会直接返回给用户,否则就会把这些 chunk 分别插入到对应的 bin 中。)。总结一张如图:

其实核心就是这里:

可以看到,在fakechunk的fd位置(64位下是fake_chunk+0x10)写入了unsorted_bin的地址(unsorted_chunks (av))

ubattack有很多种用法,比如错位写一个0x7f,给fastbin attack打配合(打free_hook的时候能用到)等等;比如在没有del的情况下踩出一个main_arena的地址等等orz。

注意:unsorted_chunks (av)写入之后这个位置指向的是top chunk

举个例子:

此时ubattack写入了0x00007ffff7dd1b78

可以看到,依次是:top chunk->last_remainder->fd->bk

glibc2.23,且只有new和edit,没有free和show。

Edit的时候没有检查index合法性,导致可以越界写。

在0x6020C0+0x108储存我们申请chunk的size。由于使用了strdup,所以分配的大小实际是由我们输入决定的,跟size无关,但是edit的时候是根据我们输入的size读入的。所以这里存在溢出

然后限制add的时候输入的index不能大于2,但是没关系我们一直add index=0就行,反正是strdup开chunk

打印依次是:Hello 6 2

https://ama2in9.top/2019/12/12/hhb/

https://l0ne1y.xyz/2020/05/10/Unsorted_Bin_Attack/#more

https://xz.aliyun.com/t/7251#toc-7

ptr1:0x602010
ptr3:0x602100
我们先free1再free3:
 
unsortbin: 0x6020f0 (size : 0xc0) <--> 0x602000 (size : 0xc0)
ptr1:0x602010
ptr3:0x602100
我们先free1再free3:
 
unsortbin: 0x6020f0 (size : 0xc0) <--> 0x602000 (size : 0xc0)
addr                prev                size                 status              fd                bk               
0x602000            0x0                 0xc0                 Freed     0x7ffff7dd1b78          0x6020f0
 
0x6020f0            0x0                 0xc0                 Freed           0x602000    0x7ffff7dd1b78
addr                prev                size                 status              fd                bk               
0x602000            0x0                 0xc0                 Freed     0x7ffff7dd1b78          0x6020f0
 
0x6020f0            0x0                 0xc0                 Freed           0x602000    0x7ffff7dd1b78
 
 
 
 
 
while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))    //从unsortbin第一个chunk(队尾)开始顺着bk指针向前遍历
        {
          bck = victim->bk;                        //bck是倒数第二个,victim是倒数第一个
          //size check
          if (__builtin_expect (victim->size <= 2 * SIZE_SZ, 0)
              || __builtin_expect (victim->size > av->system_mem, 0))
            malloc_printerr (check_action, "malloc(): memory corruption",
                             chunk2mem (victim), av);
          size = chunksize (victim);            //取出最victim的size
 
          /*
             If a small request, try to use last remainder if it is the
             only chunk in unsorted bin.  This helps promote locality for
             runs of consecutive small requests. This is the only
             exception to best-fit, and applies only when there is
             no exact fit for a small chunk.
           */
          //last remainder first
          if (in_smallbin_range (nb) &&
              bck == unsorted_chunks (av) &&
              victim == av->last_remainder &&                //如果我们申请的size在smallbin的范围,且last_remainder是unsortedbin中的唯一chunk,那么优先使用last_remainder(这个跟我们usortbin切割相关)
              (unsigned long) (size) > (unsigned long) (nb + MINSIZE))
            {
              /* split and reattach remainder */
              remainder_size = size - nb;
              remainder = chunk_at_offset (victim, nb);
              //cut and put the remained part back to unsorted list
              unsorted_chunks (av)->bk = unsorted_chunks (av)->fd = remainder;
              av->last_remainder = remainder;
              remainder->bk = remainder->fd = unsorted_chunks (av);
              if (!in_smallbin_range (remainder_size))
                {
                  remainder->fd_nextsize = NULL;
                  remainder->bk_nextsize = NULL;
                }
 
              set_head (victim, nb | PREV_INUSE |
                        (av != &main_arena ? NON_MAIN_ARENA : 0));
              set_head (remainder, remainder_size | PREV_INUSE);
              set_foot (remainder, remainder_size);
 
              check_malloced_chunk (av, victim, nb);
              void *p = chunk2mem (victim);
              alloc_perturb (p, bytes);
              //return to user
              return p;
            }
 
          /* remove from unsorted list */
          //unsorted bin attack在这里
          //unsorted_chunks (av)这里就是victim即最后一个chunk,当我们可以控制victim的bk指针时(即bck)
          //就可以往bck->fd写入unsorted_chunks(av)即*(bck+0x10)=unsorted(av)
          //要求bck是一个可写的地址
          unsorted_chunks (av)->bk = bck;
          bck->fd = unsorted_chunks (av);
 
          /* Take now instead of binning if exact fit */
            //如果请求的nb和victim的大小刚好符合,直接返回给用户
          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;
 
#define MAX_ITERS       10000
          if (++iters >= MAX_ITERS)
            break;
        }
while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))    //从unsortbin第一个chunk(队尾)开始顺着bk指针向前遍历
        {
          bck = victim->bk;                        //bck是倒数第二个,victim是倒数第一个
          //size check
          if (__builtin_expect (victim->size <= 2 * SIZE_SZ, 0)
              || __builtin_expect (victim->size > av->system_mem, 0))
            malloc_printerr (check_action, "malloc(): memory corruption",
                             chunk2mem (victim), av);
          size = chunksize (victim);            //取出最victim的size
 
          /*
             If a small request, try to use last remainder if it is the
             only chunk in unsorted bin.  This helps promote locality for
             runs of consecutive small requests. This is the only
             exception to best-fit, and applies only when there is
             no exact fit for a small chunk.
           */
          //last remainder first
          if (in_smallbin_range (nb) &&
              bck == unsorted_chunks (av) &&
              victim == av->last_remainder &&                //如果我们申请的size在smallbin的范围,且last_remainder是unsortedbin中的唯一chunk,那么优先使用last_remainder(这个跟我们usortbin切割相关)
              (unsigned long) (size) > (unsigned long) (nb + MINSIZE))
            {
              /* split and reattach remainder */
              remainder_size = size - nb;
              remainder = chunk_at_offset (victim, nb);
              //cut and put the remained part back to unsorted list
              unsorted_chunks (av)->bk = unsorted_chunks (av)->fd = remainder;
              av->last_remainder = remainder;
              remainder->bk = remainder->fd = unsorted_chunks (av);
              if (!in_smallbin_range (remainder_size))
                {
                  remainder->fd_nextsize = NULL;
                  remainder->bk_nextsize = NULL;
                }
 
              set_head (victim, nb | PREV_INUSE |
                        (av != &main_arena ? NON_MAIN_ARENA : 0));
              set_head (remainder, remainder_size | PREV_INUSE);
              set_foot (remainder, remainder_size);
 
              check_malloced_chunk (av, victim, nb);
              void *p = chunk2mem (victim);
              alloc_perturb (p, bytes);
              //return to user
              return p;
            }
 
          /* remove from unsorted list */
          //unsorted bin attack在这里
          //unsorted_chunks (av)这里就是victim即最后一个chunk,当我们可以控制victim的bk指针时(即bck)
          //就可以往bck->fd写入unsorted_chunks(av)即*(bck+0x10)=unsorted(av)
          //要求bck是一个可写的地址
          unsorted_chunks (av)->bk = bck;
          bck->fd = unsorted_chunks (av);
 
          /* Take now instead of binning if exact fit */
            //如果请求的nb和victim的大小刚好符合,直接返回给用户
          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;
 
#define MAX_ITERS       10000
          if (++iters >= MAX_ITERS)
            break;
        }

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 2
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//