티스토리 뷰

최종 수정: 2014-10-16


안녕하세요. Hackability 입니다.


오늘 포스팅 내용은 2014 SU CTF에서 Reverse 200pt로 출제된 2048 이라는 문제 입니다. 문제 바이너리와 내용은 다음과 같습니다.


2048.tar.gz

What is the ip of the victim

Flag is: md5(ip)


먼저, .tar.gz으로 되어 있기 때문에 다음과 같이 압축을 해제 합니다.


2048이라는 64-bit ELF 파일이 떨어졌습니다.


2048은 숫자를 상하좌우로 움직이면서 2048이라는 숫자로 만드는 게임인데 상당히 중독성도 있고 재미있으니 한번 플레이 해보시길 바랍니다. 안드로이드의 경우에는 2048 이라고 구글 플레이 스토어에 검색하시면 무료로 즐기실 수 있습니다. 실제로 요즘 CTF에서 2048을 주제로한 문제들이 많이 나오고 있기 때문에 한 번 알아보시는 것도 좋을 것 같습니다.


2048을 살펴 보면 다음과 같습니다.


내용을 IDA로 살펴 보면 fork 를 통해 부모 프로세스에서는 run_game이 동작하고 child 프로세스에서는 run_bot 이라는 내용이 동작하게 되어 있습니다. 좀더 분석해보면 run_game의 경우, 실제로 게임에 대한 로직만 있고 실제로 문제에 관련된 내용은 run_bot에 있음을 알 수 있습니다.


run_bot을 좀 더 상세히 살펴 보면 다음과 같이 구성되어 있음을 알 수 있습니다.


run_bot의 초기 시작 부분인데, 이 부분에서는 check_time함수를 호출하고 return 값이 1이면 attack함수로 넘어가고 1이 아니라면 sleep을 하게 됩니다. 우리가 원하는 로직은 attack으로 뛰어야 하는 것 같습니다. 그렇다면 check_time에서는 언제 1을 리턴 하는지 살펴 보도록 하겠습니다.


위 내용을 살펴 보면 time 함수를 호출하여 현재 시간을 초 단위로 환산을 하고 (UNIX TIME) time_1의 값과 비교 합니다. 만약 time_1 상수 값이랑 현재 시간과 같다면 eax (리턴 값)을 1로 설정하고 그렇지 않다면 0xFFFFFFFF(-1)이 리턴이 됩니다. IDA로 확인하거나 gdb로 확인해보면 time_1의 값이 0x52C35A80 임을 알 수 있습니다.


이 시간을 찍어 보면 2014년 1월 1일 0시 0분 0초 임을 알 수 있습니다.

#date --utc -ud @`(echo $((0x52c35a80)))`


이를 통해 우리는 시간을 2014-01-01 00:00:00 UTC로 시간을 설정하면 attack 함수로 진입할 수 있음을 알 수 있습니다. 그러면 attack 함수는 어떤 역할을 하는지 살펴 보도록 하겠습니다. attack 함수를 gdb로 살펴 보면 다음과 같습니다.


time, localtime 등을 호출 한 뒤, MD5 등의 연산을 수행합니다. 그 뒤의 내용은 다음과 같습니다.


중간 중간에, ascii 대역의 값들이 대입됨을 볼 수 있습니다. 함수 내에서 이런 내용들을 모아서 보면 다음과 같습니다.


wget http://ctf.sharif.edu:2048/?/target.txt


? 로 된 것은 아직 값을 알 수 없는 내용입니다. 결과적으로 저 부분에는 md5된 값이 들어 가게 되는데요. 저 스트링은 system함수의 인자로 들어 가게 됩니다. 그러면 동적분석을 통해 어떻게 동작하는지 살펴 보도록 하겠습니다.


이 프로그램에서는 fork 통해 만들어진 child 프로세스에 관심이 있기 때문에 gdb에서 child 프로세스를 따라가려면 다음과 같이 설정해 줍니다. (#set follow-fork-mode child)


실행을 하기 전에 먼저 설정해야 할 것은, child 프로세스 (run_bot)에서 attack 함수로 들어가기 위한 조건으로 처음에 time 함수가 호출되었을 때 값이 0x52c35a80과 같아야 했기 때문에 그 부분을 맞춰 줍니다. 아래와 같이 check_time 함수에서 cmp 해주는 부분에 break point를 걸어줍니다.


(gdb) r


위 명령을 통해 실행하면 게임 화면이 보이고 가만히 서 있는데, 엔터 한번 쳐주면 (gdb) 프롬프트가 대기하고 있음을 알 수 있고 우리가 설정한 bp 에 멈춰 있음을 알 수 있습니다. 이 때 레지스터 값을 확인하여 edx를 ecx와 동일하게 설정해 줍니다.


그리고 계속 진행 해봅니다. gdb 에서 c는 continue의 약자로 bp 의 내용을 계속 진행하라는 의미 입니다.


(gdb) c


c 를 몇번 치다 보면 다음과 같은 화면을 볼 수 있습니다. 이 부분은 child 에서 출력되는 것 이기 때문에 만약 set follow-fork-mode child를 하지 않았다면 볼 수 없는 메시지 입니다.


중간에 보면 wget http://ctf.sharif.edu:2048/md5 value/target.txt를 함을 알 수 있는데 결과 값이 404: Not Found로 나왔음을 알 수 있습니다.


attack 모듈을 좀 더 자세히 살펴 보면 md5를 위한 몇 가지 연산을 하게 되는데 이 때 time 함수 기반으로 생성되게 됩니다. 우리가 bp를 통해 check_time은 우회할 수 있었지만, 그 뒤의 attack 함수에서 다시 time을 호출하기 때문에 정확한 md5계산이 되지 않았던 것 같습니다. 또한 attack 함수에서 뭔가 추가적인 연산을 하기도 합니다.


이를 해결하기 위해 지속적으로 레지스터 값을 바꾸거나, 조건등을 살펴 봐서 해쉬 로직을 분석하는 방법도 있겠지만 먼저, 가장 쉬운 방법으로 리눅스 시간을 2014-01-01 00:00:00 UTC 로 설정하고 프로그램을 실행 시켜 보도록 하겠습니다. 이를 하기 위한 커맨드 명령은 다음과 같습니다.


#date -s @1388534400 | ./2048


date는 날짜 시간을 출력해 주는 프로그램으로 -s 옵션을 주면 해당 시간으로 세팅 할 수 있습니다. 1388534400은 0x52c35a80을 10진수로 표현한 값이고 설정 후 2048을 실행 시켜 주었습니다. 결과는 다음과 같습니다.


프로그램 출력이 뭔가 지저분하게 되었지만, 그래도 wget은 200 OK를 뱉은것을 보니 정확한 URL을 접근 한 것 같습니다. 출력을 보면 정답을 가리키고 있는 URL은 다음과 같습니다.


http://ctf.sharif.edu:2048/7266de4447eba9a354622271cff2dde5/target.txt


떨어진 파일을 열어 보면 IP 값이 들어 있고 이 값을 md5하면 정답이 되게 됩니다.

#cat target.txt

#perl -e 'print "95.211.102.203" | md5sum


FLAG: 5cfed0f91a2b71a5d79aa681eaf10adb

댓글