how2heap-2.23-08-large_bin_attack

pwngdb命令,方便源码调试

set context-sections code
set context-source-code-lines 40

粗略理解chunk进入large bin的过程(先看图解,再回头看代码)

下面是从unsorted bin链中取出的chunk,插入到large bin链的代码
how2heap-2.23-08-large_bin_attack_第1张图片
基本逻辑如下:

  • 从unsorted bin链中取出的chunk大小,是否属于small bin的大小
    • small bin相关的处理
  • 从unsorted bin链中取出的chunk大小,是否属于large bin的大小
    • 计算出当前chunk大小从属的main_arean->bins的下标
    • 获取该bins下标的large bin的头结点 bck
    • 通过large bin头节点的fd,找到large bin链中size最大的chunk fwd(large bin链中第一个chunk)
    • 当前large bin链不为空(操作的是fd_nextsize和bk_nextsize形成的链)
      • 当前chunk的size < large bin链中最小的chunk
        • 总结:将当前chunk插入到large bin链的尾部,即插入到最小的chunk的后面
        • fwd = bck; 令 fwd 指向 large bin 头结点
        • bck = bck->bk; 令 bck 指向 largin bin 尾部 chunk,就是当前已在large bin链中最小的这个chunk
        • victim->fd_nextsize = fwd->fd; 当前chunk 的 fd_nextsize 指向 largin bin 的第一个 chunk
        • victim->bk_nextsize = fwd->fd->bk_nextsize; 当前chunk的 bk_nextsize 指向原来链表的第一个 chunk 指向的 bk_nextsize(当前chunk的bk_nextsize指向原先最小的chunk)
        • fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
          • victim->bk_nextsize->fd_nextsize = victim; 原先最小chunk的fd_nextsize指向当前chunk
          • fwd->fd->bk_nextsize = victim large bin中第一个chunk的bk_nextsize指向当前chunk
      • 当前chunk的size >= large bin链中最小的chunk
        • 从large bin链中size最大的chunk fwd(large bin链中第一个chunk),从大到小遍历,找到首个不大于当前chunk的chunk
        • 如果找到的chunk的大小等于当前chunk
          • fwd = fwd->fd;将当前chunk 插入到该chunk的后面,并不修改 nextsize 指针
        • 如果找到的chunk的大小小于当前chunk
          • victim->fd_nextsize = fwd; 当前chunk的fd_nextsize指向这个找到的chunk
          • victim->bk_nextsize = fwd->bk_nextsize; 当前chunk的bk_nextsize指向这个找到的chunk的bk_nextsize
          • fwd->bk_nextsize = victim; 找到chunk的bk_nextsize指向当前chunk
          • victim->bk_nextsize->fd_nextsize = victim; 找到chunk原先前面的chunk的fd_nextsize指向当前chunk
        • 获取找到的chunk的前面的一个chunk(通过bk找到当前chunk插入前,最小的比找到的chunk大的chunk)
    • 当前large bin链为空(操作的是fd_nextsize和bk_nextsize形成的链)
      • 当前chunk的fd_nextsizebk_nextsize均指向自己
  • 将当前chunk链入large bin链中(fd,bk形成的链

图解入large bin链的逻辑

large bin链中放入首个chunk:chunk a

how2heap-2.23-08-large_bin_attack_第2张图片

这里fd,bk,fd_nextsize,bk_nextsize指向都是chunk的首地址


放入 size 比 chunk a 大的 chunk c

how2heap-2.23-08-large_bin_attack_第3张图片

large bin fd总是指向size最大的chunk
large bin bk总是指向size最小的chunk
large bin链中的chunk,通过large bin fd 从大到小排序


放入size 比 chunk a 小的 chunk b

how2heap-2.23-08-large_bin_attack_第4张图片
large bin链中的chunk,通过large bin fd 从大到小排序
只不过在代码实现中,通过large bin bk 快速来实现这一步


放入 size 和 chunk a 一样的 chunk d

how2heap-2.23-08-large_bin_attack_第5张图片
在代码中,有这么一个注释/* Always insert in the second position. */,就是大小相同的chunk,紧临着相同大小首个进入large bin链的chunk放置(可以看下面chunk e一起理解
大小相同(重复)的chunk,不会有fd_nextsize,bk_nextsize(忘掉横向链,竖向链


放入 size 和 chunk a 一样的 chunk e

how2heap-2.23-08-large_bin_attack_第6张图片
如上情况就是入large bin链的所有逻辑


回顾一下unsafe_unlinkunsorted_bin_attack

how2heap-2.23-05-unsorted_bin_attack
how2heap-2.23-06-unsorted_bin_into_stack

unsafe_unlink 是因为一个被破坏的chunk脱链,重新修整指针时引发的问题
unsorted_bin_attack 也是一个被破坏的chunk脱链,重新修整指针时引发的问题

large_bin_attack 原理与上面的两个差不多,是一个chunk入链,与一个被破坏的chunk重新修整指针时引发的问题

从large_bin_attack被挖掘出的思路出发只考虑2.23的源码

unsorted bin chunk进入large bin链的四种逻辑:

  • large bin链为空,chunk进入large bin链
  • chunk的size比large bin链中size最小的chunk还要小
  • large bin中存在和chunk size一样大小的chunk
  • chunk的size大于large bin链中size最小的chunk

large bin chunk出large bin链

  • 从恰好大小合适的largebin中获取chunk,发生unlink
  • 从比malloc要求大的largebin中取chunk,发生unlink

好像入链归于large_bin_attack,出链归于unsafe_unlink,不清楚,只看入链的逻辑


下面通过被破坏的large bin chunk实现漏洞利用的角度来观察不同入链的逻辑(一般是堆溢出修改large bin的fd,bk,fd_nextsize,bk_nextsize

1)首个chunk入large bin链

how2heap-2.23-08-large_bin_attack_第7张图片

  • 此时还没有被破坏的chunk
  • large bin fd,large bin bk 指向这个chunk的首地址
  • chunk的fd,bk指向large bin 头结点的首地址
  • fd_nextsize,bk_nextsize指向chunk自身

这里没有利用场景

2)比最小的chunk还小的chunk入large bin链

先看看该入链逻辑所涉及的代码,寻找可被破坏-用于利用的chunk(从而有堆溢出产生时,构造这种堆结构)以及利用方式

else
  {
    victim_index = largebin_index (size);
    bck = bin_at (av, victim_index);1】
    fwd = bck->fd;2if (fwd != bck)
      {
        if ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk))
          {
            fwd = bck;3】
            bck = bck->bk;4】

            victim->fd_nextsize = fwd->fd;5】
            victim->bk_nextsize = fwd->fd->bk_nextsize;6】
            fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;7}

mark_bin (av, victim_index);
victim->bk = bck;8】
victim->fd = fwd;9】
fwd->bk = victim;10】
bck->fd = victim;11
  • 【0】victim为要入链的chunk
  • 【1】bck为large bin链的头结点,这个位置是一个固定死的值(除非篡改了arena的位置
  • 【2】fwd为large bin链中size最大的chunk,也就是紧挨着large bin头结点的chunk
  • 【3】fwd为large bin链的头结点
  • 【4】bck为large bin链中size最小的chunk

现在bck,victim,fwd,三要素集齐,下面就是构造如下的双链
how2heap-2.23-08-large_bin_attack_第8张图片
考虑到fwd时large bin链的头结点,实际的图形如下的两种形式
how2heap-2.23-08-large_bin_attack_第9张图片

  • 【5】victim->fd_nextsize = fwd->fd; fwd为large bin链的头结点,fwd->fd指向的是一个chunk的地址,即size最大的chunk(如果原先large bin链中只有一个chunk,就是指向的那个size最小的chunk的地址),这里没有办法伪造

  • 【6】victim->bk_nextsize = fwd->fd->bk_nextsize; 与【5】中描述的类似,fwd->fd指向的是与large bin头节点最近的chunk,而 fwd->fd->bk_nextsize是该chunk中的数据,如果有堆溢出漏洞,就可以篡改这个chunk的bk_nextsize字段,并赋值给要链入的chunk的bk_nextsize字段
    how2heap-2.23-08-large_bin_attack_第10张图片

  • 【7】fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;

分成两部分看
fwd->fd->bk_nextsize = victim
仅是给紧挨着large bin头结点的chunk的bk_nextsize字段赋值
how2heap-2.23-08-large_bin_attack_第11张图片


victim->bk_nextsize->fd_nextsize = victim
对篡改的bk_nextsizechunk的fd_nextsize字段赋值为victim
how2heap-2.23-08-large_bin_attack_第12张图片


如果有一个unsigned long stack_var = 0;变量,将篡改的bk_nextsize赋值为(unsigned long )(&stack_var -4),那就可以修改这个变量的值了
how2heap-2.23-08-large_bin_attack_第13张图片


【8】,【9】,【10】,【11】都是不能控制的


下面是相关代码,可以自己调试看看

#include 
#include 

// OK
int main()
{
    printf("begin test\n");
    unsigned long stack_var = 0;

    size_t *p1 = malloc(0x410); //largebin
    malloc(0x10); //to separate

    size_t *p2 = malloc(0x400); //unsortedbin
    malloc(0x10); //to separate
    free(p1);
    malloc(0x470); //Release p1 into largebin
    free(p2);  //Release p2 into unsortedbin

    p1[3] = (unsigned long )(&stack_var -4);
    malloc(0x470);
    return 0;
}
#include 
#include 

// OK
int main()
{
    printf("begin test\n");
    unsigned long stack_var = 0;

    size_t *p1 = malloc(0x420);
    malloc(0x10);

    size_t *p2 = malloc(0x410);
    malloc(0x10);

    size_t *p3 = malloc(0x400);
    malloc(0x10);
    free(p1);
    free(p2);
    malloc(0x470);
    free(p3);

    p1[3] = (unsigned long )(&stack_var -4);
    malloc(0x470);
    return 0;
}

3)放入相同大小的 chunk

所涉及的代码如下

    victim_index = largebin_index (size);
    bck = bin_at (av, victim_index);1】
    fwd = bck->fd;2if (fwd != bck)
      {
        size |= PREV_INUSE;
        if ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk))
          {
          }
        else
          {
            while ((unsigned long) size < chunksize_nomask (fwd))3{
                fwd = fwd->fd_nextsize;
              }
            if ((unsigned long) size == (unsigned long) chunksize_nomask (fwd))
              /* Always insert in the second position.  */
              fwd = fwd->fd;4else
              {
              }
            bck = fwd->bk;5}
      }
  }

mark_bin (av, victim_index);
victim->bk = bck;6】
victim->fd = fwd;7】
fwd->bk = victim;8】
bck->fd = victim;9
  • 【1】bck为large bin链的头结点,这个位置是一个固定死的值(除非篡改了arena的位置

  • 【2】fwd为large bin链中size最大的chunk,也就是紧挨着large bin头结点的chunk

  • 【3】通过fwd从大到小,寻找到第一个不大于victim size的chunk
    如下就是fwd和victim
    how2heap-2.23-08-large_bin_attack_第14张图片

  • 【4】如果fwd和victim大小相等,就会把fwd中的fd取出来用,从上图可以可以想到,假设存在堆溢出漏洞,那就可以修改fwd->fd
    how2heap-2.23-08-large_bin_attack_第15张图片
    经过赋值,伪造的fd指向的chunk,成为fwd
    how2heap-2.23-08-large_bin_attack_第16张图片

  • 【5】将伪造的fwd的bk取出,作为bck
    how2heap-2.23-08-large_bin_attack_第17张图片

这里有个问题,伪造的fwd chunk的bk字段必须是要有内容的,最好是个可写内存的地址,否则执行【9】时程序会崩溃


【6】,【7】给victim chunk的bk,fd赋值,这个不管
【8】,伪造的fwd chunk的bk赋值为victim chunk的地址
【9】,这个太不稳定了,不管
how2heap-2.23-08-large_bin_attack_第18张图片


这种攻击场景,适用于覆盖栈中保存的堆地址的变量,将其保存的堆地址修改为victim chunk的地址

#include 
#include 

int main()
{
    printf("begin\n");
    char* test_chunk = malloc(0x1000);

    size_t * a = malloc(0x410);
    malloc(0x8);

    size_t *b = malloc(0x410);
    size_t * victim = malloc(0x400);
    malloc(0x8);
    free(a);
    malloc(0x470);
    free(b);

    a[0] = (unsigned long )(&test_chunk -3);
    printf("before vuln\n");
    malloc(0x470);
    printf("after vuln\n");
    memset(test_chunk, 'a', 0x900);
    char show_array[100];
    memcpy(show_array, victim+20,30);
    printf("%s\n",show_array);
    return 0;
}

4)放入不和其他chunk size相等,且不是最小的chunk

所涉及的代码如下

    victim_index = largebin_index (size);
    bck = bin_at (av, victim_index);1】
    fwd = bck->fd;2if (fwd != bck)
      {
        size |= PREV_INUSE;
        if ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk))
          {
          }
        else
          {
            while ((unsigned long) size < chunksize_nomask (fwd))3{
                fwd = fwd->fd_nextsize;
              }

            if ((unsigned long) size == (unsigned long) chunksize_nomask (fwd))

            else
              {
                victim->fd_nextsize = fwd;4】
                victim->bk_nextsize = fwd->bk_nextsize;5】
                fwd->bk_nextsize = victim;6】
                victim->bk_nextsize->fd_nextsize = victim;7}
            bck = fwd->bk;8}
      }
    else
  }

mark_bin (av, victim_index);
victim->bk = bck;9】
victim->fd = fwd;10】
fwd->bk = victim;11】
bck->fd = victim;12
  • 【1】bck为large bin链的头结点,这个位置是一个固定死的值(除非篡改了arena的位置
  • 【2】fwd为large bin链中size最大的chunk,也就是紧挨着large bin头结点的chunk
  • 【3】通过fwd从大到小,寻找到第一个不大于victim size的chunk

如下就是fwd和victim
how2heap-2.23-08-large_bin_attack_第19张图片

假设存在堆溢出漏洞,可以溢出修改fwd中的数据
how2heap-2.23-08-large_bin_attack_第20张图片

  • 【4】victim->fd_nextsize = fwd,victim的fd_nextsize存储fwd chunk的首地址,这个没有办法伪造

  • 【5】victim->bk_nextsize = fwd->bk_nextsize,victim的bk_nextsize存储fwd chunk中伪造的bk_nextsize

  • 【6】fwd->bk_nextsize = victim,fwd的bk_nextsize字段赋值为victim chunk的首地址,这个正常
    how2heap-2.23-08-large_bin_attack_第21张图片

  • 【7】victim->bk_nextsize->fd_nextsize = victim,将伪造的bk_nextsize chunk的fd_nextsize字段覆盖为victim chunk的首地址
    how2heap-2.23-08-large_bin_attack_第22张图片

  • 【8】bck = fwd->bk,bck指向fwd伪造的bk
    how2heap-2.23-08-large_bin_attack_第23张图片
    【8】和【9】,给victim chunk 的fd和bk赋值
    【11】给fwd的bk字段赋值

  • 【12】给伪造的bk chunk的fd字段赋值为victim chunk的首地址
    how2heap-2.23-08-large_bin_attack_第24张图片

如果存在两个变量(一个也行)stack_var1,stack_var2
通过堆溢出漏洞,将fwd的bk修改为&stack_var1-2,fwd的bk_nextsize修改为&stack_var2-4,则stack_var1和stack_var2存储的内容在漏洞触发后都能修改为victim chunk的首地址
how2heap-2.23-08-large_bin_attack_第25张图片


测试代码如下

#include 
#include 

int main()
{
    printf("begin test\n");
    unsigned long stack_var1 = 0;
    unsigned long stack_var2 = 0;

    size_t * p1 = malloc(0x410);
    malloc(0x8);

    size_t * p2 = malloc(0x420);
    malloc(0x8);
    free(p1);
    malloc(0x470);
    free(p2);

    p1[1] = (unsigned long )(&stack_var1 - 2);
    p1[3] = (unsigned long )(&stack_var2 - 4);

    malloc(0x470);

    return 0;
}

现在how2heap中的large_bin_attack应该可以完全理解了

你可能感兴趣的:(二进制安全-01-pwn,linux,pwn)