elf之link map攻击
字数 2063 2025-08-23 18:31:25
ELF Link Map 攻击技术详解
1. 基本概念
1.1 ELF 文件与 Link Map
ELF (Executable and Linkable Format) 文件中的 Link Map 是链接器生成的一个详细的内存布局文件,展示了链接过程中各个段的地址、大小和符号表信息。它描述了程序在内存中的布局,包括:
- 哪些符号被导入、导出
- 各个节(如 .text、.data、.bss)的内存地址
- 依赖库的加载顺序等
1.2 攻击类型
在 pwn 领域,有两种专门针对 link map 的攻击方式:
- 栈上的 ret2dl-resolve 攻击
- 堆上的 house of banana 攻击
2. ret2dl-resolve 攻击
2.1 基本特征
- 最大特征:不提供 libc
- 不能使用 patchelf 修改 elf 文件,因为会移动延迟绑定相关的结构
2.2 关键结构
利用的相关结构主要有四个:
-
.dynamic 段
- 作用:包含动态链接器需要的配置信息
- 内容:存储一系列键值对,如依赖的共享库(DT_NEEDED)、重定位表位置(DT_REL/DT_RELA)等
-
.dynstr 段
- 作用:存储动态符号表中用到的符号和库的字符串信息
- 内容:包含动态符号的名称字符串和共享库名称字符串
-
.dynsym 段
- 作用:动态符号表,包含所有动态符号(如全局变量和函数)的相关信息
- 内容:每个条目包含符号的名称、类型、地址等
-
.rel.plt 段
- 作用:包含与PLT相关的重定位信息,用于延迟绑定
- 内容:存储了待重定位符号的索引和重定位类型
2.3 延迟绑定过程
- 调用PLT表:首次调用外部函数时跳转到PLT表入口
- PLT跳转到.rel.plt中的重定位项
- 读取.dynsym和.dynstr找到函数实际地址
- 完成解析并将地址写入GOT
2.4 关键结构体
Dyn 结构体
typedef struct {
Elf32_Sword d_tag; // Dynamic entry type
union {
Elf32_Word d_val; // Integer value
Elf32_Addr d_ptr; // Address value
} d_un;
} Elf32_Dyn;
Sym 结构体
typedef struct {
Elf32_Word st_name; // Symbol name (string tbl index)
Elf32_Addr st_value; // Symbol value
Elf32_Word st_size; // Symbol size
unsigned char st_info; // Symbol type and binding
unsigned char st_other; // Symbol visibility
Elf32_Section st_shndx; // Section index
} Elf32_Sym;
Rel 结构体
typedef struct {
Elf32_Addr r_offset; // Address
Elf32_Word r_info; // Relocation type and symbol index
} Elf32_Rel;
link_map 结构体
struct link_map {
ElfW(Addr) l_addr; // Difference between the address in the ELF file and the addresses in memory
ElfW(Dyn) *l_info[DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM + DT_ADDRNUM];
// ... other members
};
2.5 _dl_fixup 函数分析
_dl_fixup 是延迟绑定机制的核心函数,其关键逻辑:
- 获取符号表地址
- 获取字符串表地址
- 获取函数对应的重定位表结构地址
- 获取函数对应的符号表结构地址
- 判断重定位表类型必须为 ELF_MACHINE_JMP_SLOT(7)
- 根据
ELFW(ST_VISIBILITY)(sym->st_other)的值决定重定位策略
2.6 32位程序攻击方法
方法一:改写.dynamic的DT_STRTAB
- 条件:NO RELRO(.dynamic可写)
- 原理:修改.dynstr字符串表指针到可控内存,伪造函数名
方法二:操纵第二个参数伪造Elf32_Rel
- 条件:.dynamic不可写时使用
- 原理:伪造.rel.plt、.dynsym和.dynstr结构
2.7 64位程序攻击方法
64位下由于地址范围问题,选择 ELFW(ST_VISIBILITY)(sym->st_other) 不为0时的执行流程:
- resolve函数传入第二个参数为0
- Elf64_Rel的r_offset加上link_map->l_addr指向可读写内存
- Elf64_Rel的r_info的低32位设为7
- l_info[5]指向可读写内存
- r_info的高32位设为0
- link_map->l_info[6]->d_un.dptr指向puts@got - 8
- link_map的l_addr设置为&system - &puts
3. house of banana 攻击
3.1 基本概念
- 劫持的是rtld_global结构体中的link_map指针
- 类似于exit_hook攻击,但exit_hook在2.34之后失效
3.2 rtld_global 结构
struct rtld_global {
// ...
struct link_namespaces {
struct link_map *_ns_loaded;
unsigned int _ns_nloaded;
struct r_scope_elem *_ns_main_searchlist;
// ... other members
} _dl_ns[DL_NNS];
size_t _dl_nns;
// ... other members
};
3.3 _dl_fini 函数逻辑
- 遍历_dl_ns数组
- 将_ns_loaded链表中的元素放入maps数组
- 遍历maps数组,调用符合条件的函数
3.4 伪造link_map的关键点
- 令l_real指针指向link_map结构体自身
- link_map链表中的元素个数为4(_ns_nloaded默认为4)
- l_init_called位置1
- 绕过_dl_sort_maps函数的检查
3.5 示例利用代码
from pwn import *
context.log_level = 'debug'
io = process('./pwn')
elf = ELF('./pwn')
libc = ELF('libc-2.27.so')
# ... 省略部分代码 ...
rtld_global = libc_base + 0x62a060
link_map3 = rtld_global + 0x1ccfb8
one_gadget = libc_base + 0x4f302
# 伪造link_map结构
fake_addr = heap_base + 0xb90
payload = p64(0)*3 + p64(fake_addr)
payload = payload.ljust(0x48-0x10, b'\x00') + p64(fake_addr+0x58) + p64(8) + p64(one_gadget)
payload = payload.ljust(0x110-0x10, b'\x00') + p64(fake_addr+0x40)
payload = payload.ljust(0x120-0x10, b'\x00') + p64(fake_addr+0x48)
payload = payload.ljust(0x314-0x10, b'\x00') + p64(0x1c)
# 触发exit调用链
io.sendlineafter('Your choice:\n', str(5))
io.interactive()
4. 防御措施
- Full RELRO:使.dynamic段不可写
- 地址随机化(ASLR):增加预测地址难度
- 堆保护机制:检测堆结构完整性
- 高版本glibc中对rtld_global的保护
5. 总结
ELF link map攻击技术提供了在特定条件下绕过常规防护的手段:
- ret2dl-resolve适用于无libc泄露的场景
- house of banana适用于高版本glibc的堆利用
- 两种方法都需要对ELF结构和动态链接过程有深入理解
理解这些攻击技术有助于更好地设计防御措施和编写安全的代码。