unlink
unlink
山林川泽1. 核心原理:glibc 的堆块管理机制
在 glibc
中,空闲堆块通过双向链表组织(fd
和 bk
指针)
1 | struct malloc_chunk { |
当释放堆块时,glibc
会执行 unlink
操作将其从空闲链表移除:
1 | // 简化版 unlink 宏 |
2. 攻击条件
- 堆溢出漏洞:可覆盖相邻堆块的头部数据(
prev_size
和size
) - 可控内存:能伪造堆块结构(控制
fd
和bk
指针) - 触发 unlink:需通过
free()
或堆合并触发目标堆块的unlink
操作
3. 攻击步骤图解
步骤 1:伪造堆块结构
假设存在堆块 A
(易溢出)和 B
(目标),在 A
中伪造一个空闲堆块:
1 | 伪造的堆块 P |
步骤 2:修改相邻堆块头
通过堆溢出修改 B
的头部:
1 | B->prev_size = 伪造堆块大小 // 使系统认为 P 是空闲块 |
步骤 3:触发 unlink
释放堆块 B
时,glibc
会:
- 检查
B->prev_inuse=0
,认为前一块P
空闲 - 尝试合并
P
和B
,触发unlink(P)
步骤 4:unlink 任意地址写
执行 unlink
操作时:
1 | FD = P->fd = target - 0x18 |
最终 *target
被修改为 target - 0x18
。
4. 现代 glibc 的防护与绕过
防护机制(Safe-Unlinking)
1 | // glibc 2.3.6+ 的检查 |
绕过方法
构造满足检查的伪造指针
1 | P->fd = target - 0x18 |
5. 实战利用场景
场景:修改 GOT 表执行 shellcode
- 选择目标:
free@got.plt
- 构造
target = free@got.plt
- 触发
unlink
后:*free@got.plt = free@got.plt - 0x18
- 通过堆操作写
free@got.plt
区域:
1 | # 此时 free@got.plt 指向自身 -0x18 |