条件:控制smallbin中第一个chunk(假设为victm)的bk,bk指向要写的位置。bk->fd要为victim。这时malloc会从smallbin中取出chunk,同时last(bin)就为我们伪造的bk了,下次malloc时就可以得到。同时需要注意下次malloc时,还会检测bk->fd要为victim。

这个利用条件比较难满足。

0x01 原理&利用

原理,当malloc从smallbin中取出对应的chunk时,取出bin->bk,判断和bin是否不相等。初始时,arena.bins中都为0,肯定不等。

  • 如果victim为0,则代表 double link listed未初始化
  • 如果victim不为0,则代表已经初始化。

  1. check bck(victim->bk)->fd是否是victim
  2. unlink,将victim从bins中unlink。更新bin->bk和bck->fd

因此,如果伪造了victim的bk,则可以控制之后malloc的chunk位置。

伪造bk时,注意bk->fd要指向victim。同时malloc它时,同样再满足一次这个条件。具体看做的ppt。

补充一下 @wolfzhang阐述的利用时的思路。

总体来说是下面的顺序:
1.malloc(n)—->2.malloc(n)—->3.malloc(n) —->4.free(2)—->5.malloc(n+m)—->6.overflow chunk2—->7.malloc(n)—->8.malloc(n)。步骤3是为了防止步骤2的chunk和top chunk合并。步骤5是为了让chunk 2 link到相应的samllbin中。

  /*
     If a small request, check regular bin.  Since these "smallbins"
     hold one size each, no searching within bins is necessary.
     (For a large request, we need to wait until unsorted chunks are
     processed to find best fit. But for small ones, fits are exact
     anyway, so we can check now, which is faster.)
   */

  if (in_smallbin_range (nb))                                 // smallbins 20181214
    {
      idx = smallbin_index (nb);
      bin = bin_at (av, idx);                   
      if ((victim = last (bin)) != bin)                      
        {                                        
          if (victim == 0) /* initialization check */
            malloc_consolidate (av);              
          else
            {
              bck = victim->bk;                            // important
                  if (__glibc_unlikely (bck->fd != victim))   // check。
                {
                  errstr = "malloc(): smallbin double linked list corrupted";
                  goto errout;
                }
              set_inuse_bit_at_offset (victim, nb);
              bin->bk = bck;                                  
              bck->fd = bin;

              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;
            }
        }
    }

这里贴一下我画的一张图:

image-20181214215501681-0906034

0x02 how2heap

https://github.com/shellphish/how2heap/blob/master/glibc_2.25/house_of_lore.c

0x03 汇编角度看 if ((victim = last (bin)) != bin)

关于double link listed是否为空的汇编角度的说明,源码对应于if ((victim = last (bin)) != bin):

image-20181214201419015-0906031

image-20181214201303244-0906029

参考