CTF_pwn_堆入门知识及例题分析
字数 2836 2025-08-24 07:48:33

堆漏洞利用全面指南

1. 堆基础知识

1.1 堆概述

堆是一种线性分布的数据结构,与栈不同,堆由低地址向高地址伸展。堆位于bss段的高地址处,提供动态内存分配功能。Linux标准发行版使用glibc中的ptmalloc2作为堆分配器,通过malloc/free函数管理内存。

1.1.1 堆数据结构与操作

  • malloc流程

    • malloc__libc_malloc_int_malloc
    • 首先检查__malloc_hook,然后调用_int_malloc进行实际分配
  • free流程

    • free__libc_free_int_free
    • 检查是否为fastbin、mmap分配等,然后进行合并操作

1.1.2 系统调用

  • brk/sbrk:扩展堆空间的主要系统调用
  • mmap/munmap:用于大内存分配和释放

1.2 Chunk结构

struct malloc_chunk {
    INTERNAL_SIZE_T mchunk_prev_size;  // 前一个chunk的大小(如果空闲)
    INTERNAL_SIZE_T mchunk_size;       // 当前chunk大小+标志位
    struct malloc_chunk *fd;           // 前向指针(fastbins/smallbins)
    struct malloc_chunk *bk;           // 后向指针(smallbins/largebins)
    struct malloc_chunk *fd_nextsize;  // 大chunk的前向指针
    struct malloc_chunk *bk_nextsize;  // 大chunk的后向指针
};

1.3 Bin分类

  1. Fast bins

    • 单链表结构,LIFO(后进先出)
    • 大小范围:32-128字节(0x20-0x80)
    • 不合并相邻空闲chunk
  2. Small bins

    • 双向循环链表,FIFO(先进先出)
    • 大小范围:32-1024字节(0x20-0x400)
  3. Large bins

    • 双向循环链表,按大小排序
    • 大小大于1024字节(0x400)
  4. Unsorted bin

    • 双向循环链表
    • 释放的chunk先放入此处,作为"垃圾桶"

1.4 Arena

  • main_arena:主线程的堆区域
  • thread arena:子线程的堆区域,可以有多个
  • 包含堆的管理结构和各种bin的指针

2. 堆漏洞类型

2.1 堆溢出

当向堆块写入超过其可用大小的数据时发生,可以覆盖相邻堆块的数据。

利用条件

  1. 程序向堆写入数据
  2. 写入大小未正确控制
  3. 可以覆盖相邻chunk的关键数据

利用方法

  • 覆盖下一个chunk的size字段
  • 修改fd/bk指针实现任意地址读写
  • 通过unlink等机制实现控制流劫持

2.2 Use-After-Free (UAF)

释放后继续使用堆指针,分为:

  1. 指针被置NULL后使用 → 崩溃
  2. 指针未置NULL且内存未被修改 → 可能正常执行
  3. 指针未置NULL且内存被修改 → 可利用漏洞

典型利用场景

  • 释放后重新分配,修改函数指针等关键数据
  • 结合其他漏洞实现控制流劫持

2.3 Double Free

同一块内存被多次释放,可能导致:

  • 内存管理数据结构损坏
  • 实现任意地址读写

3. 堆利用技术

3.1 信息泄露

目标

  1. 堆基地址
  2. libc基地址

泄露方法

  1. 通过unsorted bin泄露

    • 当unsorted bin只有一个chunk时,其fd/bk指向main_arena
    • libc_base = fd_addr - 0x58 - 0x3c4b20 (偏移可能因版本不同)
  2. 通过fast bin泄露

    • 利用fast bin的单链表特性泄露堆地址

漏洞利用

  • 堆内存未初始化
  • 堆溢出
  • UAF
  • 越界读
  • heap extend

3.2 Fastbin Attack

原理
利用fast bin的单链表特性,通过修改fd指针实现任意地址分配。

步骤

  1. 分配并释放fast chunk
  2. 通过漏洞修改fd指针指向目标地址
  3. 再次分配获取目标地址的控制权

绕过检测

  • 目标地址的size字段需要满足:
    • 大小在fast bin范围内
    • PREV_INUSE位为1

示例

# 分配两个fast chunk
p0 = malloc(0x20)
p1 = malloc(0x20)

# 释放
free(p0)
free(p1)

# 修改p1的fd指向目标地址
# 需要确保目标地址的size字段满足条件
p1.fd = target_addr

# 重新分配
malloc(0x20)  # 得到p1
malloc(0x20)  # 得到target_addr

3.3 Unsorted Bin Attack

原理
通过修改unsorted bin中chunk的bk指针,实现向任意地址写入一个大数值。

利用场景

  • 通常用于配合fastbin attack构造合适的size字段
  • 可以用于修改全局变量等

步骤

  1. 分配并释放一个unsorted bin大小的chunk
  2. 修改其bk指针指向目标地址-0x10
  3. 再次分配触发写入

3.4 __malloc_hook攻击

原理
__malloc_hook修改为one_gadget或system地址,在调用malloc时获取shell。

步骤

  1. 泄露libc基地址
  2. 计算__malloc_hook和one_gadget地址
  3. 通过fastbin attack修改__malloc_hook
    • 通常使用__malloc_hook-0x23处的伪造size(0x7f)

3.5 House of Spirit

原理
在任意地址构造fake chunk并free,使其进入fast bin,然后重新分配实现控制。

条件

  1. 能在目标地址附近构造合法的fake chunk
  2. 能够控制free的指针

4. 实战案例分析

4.1 babyheap解题思路

  1. 泄露libc基地址

    • 创建fast chunk和unsorted chunk重叠
    • 释放unsorted chunk后通过fast chunk泄露地址
  2. fastbin attack修改__malloc_hook

    • 计算合适的fake chunk位置(__malloc_hook-0x23)
    • 通过修改fd指针实现控制
  3. 写入one_gadget

    • 计算偏移正确写入one_gadget地址

4.2 hacknote解题思路

  1. UAF漏洞利用

    • 申请两个note并释放
    • 重新申请小note覆盖函数指针
  2. 控制流劫持

    • 将打印函数指针覆盖为shell地址
    • 调用打印函数实际执行shell

4.3 easyheap解题思路

  1. House of Spirit

    • 在bss段构造fake chunk
    • 通过堆溢出修改指针
  2. GOT劫持

    • 修改free的GOT表项为system
    • 释放包含"/bin/sh"的chunk

5. 防御措施

  1. 安全机制

    • 堆Cookie
    • Safe-Linking (glibc 2.32+)
    • tcache hardening
  2. 开发建议

    • 及时置空释放的指针
    • 严格检查输入大小
    • 使用现代内存分配器
  3. 缓解技术

    • 地址随机化(ASLR)
    • 堆隔离
    • 敏感数据加密

6. 调试技巧

  1. gdb命令

    # 查看堆
    x/40gx 0x555555757000
    
    # 查看fast bins
    heap bins fast
    
    # 查看unsorted bin
    heap bins unsorted
    
  2. 关键断点

    • _int_malloc
    • _int_free
    • __malloc_hook
    • __free_hook
  3. 有用工具

    • pwndbg
    • gef
    • libc-database
    • one_gadget

7. 扩展知识

  1. tcache (glibc 2.26+):

    • 每个线程的缓存,提高分配速度
    • 引入新的安全机制和攻击面
  2. 不同glibc版本的差异

    • 安全机制增强
    • 偏移变化
    • 数据结构调整
  3. 其他堆分配器

    • jemalloc
    • mimalloc
    • tcmalloc

通过系统学习这些堆利用技术和防御措施,可以更好地理解现代堆漏洞的利用方式和防护方法。实际应用中需要结合具体环境和保护机制进行调整。

堆漏洞利用全面指南 1. 堆基础知识 1.1 堆概述 堆是一种线性分布的数据结构,与栈不同,堆由低地址向高地址伸展。堆位于bss段的高地址处,提供动态内存分配功能。Linux标准发行版使用glibc中的ptmalloc2作为堆分配器,通过malloc/free函数管理内存。 1.1.1 堆数据结构与操作 malloc流程 : malloc → __libc_malloc → _int_malloc 首先检查 __malloc_hook ,然后调用 _int_malloc 进行实际分配 free流程 : free → __libc_free → _int_free 检查是否为fastbin、mmap分配等,然后进行合并操作 1.1.2 系统调用 brk/sbrk :扩展堆空间的主要系统调用 mmap/munmap :用于大内存分配和释放 1.2 Chunk结构 1.3 Bin分类 Fast bins : 单链表结构,LIFO(后进先出) 大小范围:32-128字节(0x20-0x80) 不合并相邻空闲chunk Small bins : 双向循环链表,FIFO(先进先出) 大小范围:32-1024字节(0x20-0x400) Large bins : 双向循环链表,按大小排序 大小大于1024字节(0x400) Unsorted bin : 双向循环链表 释放的chunk先放入此处,作为"垃圾桶" 1.4 Arena main_ arena :主线程的堆区域 thread arena :子线程的堆区域,可以有多个 包含堆的管理结构和各种bin的指针 2. 堆漏洞类型 2.1 堆溢出 当向堆块写入超过其可用大小的数据时发生,可以覆盖相邻堆块的数据。 利用条件 : 程序向堆写入数据 写入大小未正确控制 可以覆盖相邻chunk的关键数据 利用方法 : 覆盖下一个chunk的size字段 修改fd/bk指针实现任意地址读写 通过unlink等机制实现控制流劫持 2.2 Use-After-Free (UAF) 释放后继续使用堆指针,分为: 指针被置NULL后使用 → 崩溃 指针未置NULL且内存未被修改 → 可能正常执行 指针未置NULL且内存被修改 → 可利用漏洞 典型利用场景 : 释放后重新分配,修改函数指针等关键数据 结合其他漏洞实现控制流劫持 2.3 Double Free 同一块内存被多次释放,可能导致: 内存管理数据结构损坏 实现任意地址读写 3. 堆利用技术 3.1 信息泄露 目标 : 堆基地址 libc基地址 泄露方法 : 通过unsorted bin泄露 : 当unsorted bin只有一个chunk时,其fd/bk指向main_ arena libc_base = fd_addr - 0x58 - 0x3c4b20 (偏移可能因版本不同) 通过fast bin泄露 : 利用fast bin的单链表特性泄露堆地址 漏洞利用 : 堆内存未初始化 堆溢出 UAF 越界读 heap extend 3.2 Fastbin Attack 原理 : 利用fast bin的单链表特性,通过修改fd指针实现任意地址分配。 步骤 : 分配并释放fast chunk 通过漏洞修改fd指针指向目标地址 再次分配获取目标地址的控制权 绕过检测 : 目标地址的size字段需要满足: 大小在fast bin范围内 PREV_ INUSE位为1 示例 : 3.3 Unsorted Bin Attack 原理 : 通过修改unsorted bin中chunk的bk指针,实现向任意地址写入一个大数值。 利用场景 : 通常用于配合fastbin attack构造合适的size字段 可以用于修改全局变量等 步骤 : 分配并释放一个unsorted bin大小的chunk 修改其bk指针指向目标地址-0x10 再次分配触发写入 3.4 __ malloc_ hook攻击 原理 : 将 __malloc_hook 修改为one_ gadget或system地址,在调用malloc时获取shell。 步骤 : 泄露libc基地址 计算 __malloc_hook 和one_ gadget地址 通过fastbin attack修改 __malloc_hook 通常使用 __malloc_hook-0x23 处的伪造size(0x7f) 3.5 House of Spirit 原理 : 在任意地址构造fake chunk并free,使其进入fast bin,然后重新分配实现控制。 条件 : 能在目标地址附近构造合法的fake chunk 能够控制free的指针 4. 实战案例分析 4.1 babyheap解题思路 泄露libc基地址 : 创建fast chunk和unsorted chunk重叠 释放unsorted chunk后通过fast chunk泄露地址 fastbin attack修改__ malloc_ hook : 计算合适的fake chunk位置( __malloc_hook-0x23 ) 通过修改fd指针实现控制 写入one_ gadget : 计算偏移正确写入one_ gadget地址 4.2 hacknote解题思路 UAF漏洞利用 : 申请两个note并释放 重新申请小note覆盖函数指针 控制流劫持 : 将打印函数指针覆盖为shell地址 调用打印函数实际执行shell 4.3 easyheap解题思路 House of Spirit : 在bss段构造fake chunk 通过堆溢出修改指针 GOT劫持 : 修改free的GOT表项为system 释放包含"/bin/sh"的chunk 5. 防御措施 安全机制 : 堆Cookie Safe-Linking (glibc 2.32+) tcache hardening 开发建议 : 及时置空释放的指针 严格检查输入大小 使用现代内存分配器 缓解技术 : 地址随机化(ASLR) 堆隔离 敏感数据加密 6. 调试技巧 gdb命令 : 关键断点 : _int_malloc _int_free __malloc_hook __free_hook 有用工具 : pwndbg gef libc-database one_ gadget 7. 扩展知识 tcache (glibc 2.26+): 每个线程的缓存,提高分配速度 引入新的安全机制和攻击面 不同glibc版本的差异 : 安全机制增强 偏移变化 数据结构调整 其他堆分配器 : jemalloc mimalloc tcmalloc 通过系统学习这些堆利用技术和防御措施,可以更好地理解现代堆漏洞的利用方式和防护方法。实际应用中需要结合具体环境和保护机制进行调整。