OutDoorFrog의 리버싱 이야기

defcon crackme 2000 sorcery 본문

공부/CTF 문제 풀이

defcon crackme 2000 sorcery

OutDoorFrog 2019. 1. 23. 05:19

아는 지인분께 반드시 풀어보는 것이 좋다고 추천 받은 좋은 문제가 있습니다.


defcon-crackme 2000 시리즈에 속한 문제들입니다.


실로 문제 난이도가 어렵더군요;;;




이 바이너리를 어디서 다운 받을 수 있을까요..


sinfocol 님의 깃허브 에서 다운받을 수 있습니다.


제가 풀어볼 문제는 sorcery라는 문제입니다.




바이너리 구성


일단 샘플 상에 20여개의 바이너리가 존재합니다. (아래의 하나는 제가 ida로 분석한 거.. ㅎㅎ)

(근데 이거 라이트업 보니까 바이너리가 200개 존재하던데 왜 샘플에는 20개지)


아마 easy crack101 류의 풀이 형태가 비슷할 것 같습니다.





문제 분석


일단 바이너리 하나만 콕 찝어서 분석해봅시다 ㅎㅎ.


저는 0a4를 접두사로 가지고 있는 바이너리를 분석해보겠습니다.


분석의 시작은 실행부터 해야 제맛이죠.


"enter code:" 라고 떠서 고백을 했더니 차였습니다. (엉엉)




여기에서 우리는 중요한 몇 가지 사실을 알 수가 있습니다!


1. 이 바이너리에는 "enter code:"를 출력해주는 함수가 존재한다.

2. 이 바이너리에는 입력을 받는 함수가 존재한다.

3. 이 바이너리의 이름은 왜이리 길게 만들어졌을꼬??

(3번은 딱히 중요하지 않을 수도..?)


IDA로 열어봅시다!


base address가 없을 때 나오는 증상입니다.


저는 이런 base address가 없는 문제들을 angr로 풀 수 없는 문제라고 생각하고 있습니다.


문제가 쉽게 풀려주진 않을 것 같네요.





main 함수


음.. 그래프 개형만 보여드리겠습니다.



그래프의 개형이 복잡하고 문자열을 출력, 입력하는 함수가 보이진 않습니다.


우리가 분석을 시작해야되는 부분은 "enter code:"가 출력되는 부분이니까..


문자열 검색을 시도해보겠습니다.



문자열 검색에서도 "enter code:"가 안뜨는군요 ㅋㅋㅋㅋ


한 번 우리가 얻었던 정보를 돌이켜봅시다.


문자열을 출력해주는 함수와 입력해주는 함수를 이 바이너리에서는 반드시 사용을 할 것입니다.


그리고 "enter code:"라는 문자열을 찾아낼 수 없습니다. 


즉 문자열 출력해주는 함수를 이용하여 찾는 방법은 우리가 분석을 시작해야할 부분을 찾는 데 

관련이 없을 가능성이 크다고 판단할 수 있습니다. 


그렇다면 입력하는 함수를 따로 찾아봐서 검색해봅시다.



_read, read 함수를 발견할 수 있었습니다.


_read 함수를 어느 주소에서 호출하는지 살펴봅시다.



sub_A6A0+28로 한 번 가봅시다.




read 함수를 호출하는 것을 확인할 수 있고, 또 어떤 함수에게서 호출당하네요. 추적해봅시다.



sub_30FC


main 함수에서 호출당하는 함수네요. 이제 조금 분석해야할 부분이 보이기 시작하는군요.






리버싱의 흐름


대부분 리버싱 문제의 흐름은 항상 이렇게 진행됩니다.


출력 -> 입력 -> 연산 -> 판단 -> 출력


여기에서는 입력하라고 출력하고, 입력받고, 플래그가 맞는지 판단하고 

플래그가 맞다면 맞다고 출력하겠네요.


(저는 주로 연산은 주로 값을 한 번 더 꼬거나, 안티 리버싱 첨가된 부분을 연산이라고 칭합니다.

아직은 연산 부분이 있는지 없는지 알 수는 없습니다.)


입력하는 부분은 찾아냈으니 판단해주는 부분을 찾으면 되겠네요?


sub_30FC에서 sub_a6a0을 출력하는 부분부터 분석해봅시다.


sub_a6a0이라는 함수를 ReadCaller라는 함수로 리네임시켜줬습니다.



디컴파일 시켜서 한 번 슈도 코드를 확인해봅시다.



좋습니다. 판단이라고 생각해볼수 있는 부분을 찾아봅시다.



글자를 하나씩 받아서 플래그 인지 아닌지 확인하는 부분이라고 의심해볼만한 코드네요..

(딱 판단 부분이라고 포스가 흐르고 있습니다.)


dec -> hex로 한 후 그대로 hxd에 써넣어 보겠습니다.



뭔가 플래그 같은 느낌이 듭니다. 한 번 입력해봅시다.



오오오 다른 반응이 나오는군요?


다른 바이너리도 확인해봅시다.


0c5..로 시작하는 바이너리를 위와 같은 방법으로 리버싱하여 코드를 넣어봤습니다.



좋습니다. 어떻게 답을 구해야될지도 알겠다.. 우리에게 남은 문제가 있습니다.




어떻게 이 작업을 자동화 시킬까?


솔직히 바이너리 20개 정도야 금방 해낼 것 같습니다.


처음 분석하는데 시간이 오래 걸릴 뿐, 구하는 방법만 알아내면 그만큼 속도가 가속되니까요.


하지만 바이너리가 100, 200개라면? 혹은 그 이상의 바이너리를 조사해야될 경우라면?


참 난해해집니다. 30분 만에 끝낼 수 있는 걸 더 시간이 낭비되겠죠?


그래서 빠르게 답을 구해내려면 어떻게 이 작업을 자동화시킬지에 대해서 고민해봐야합니다.


일단 아래 사진을 보겠습니다.



저 사진은 한 글자식 비교하는 어셈블리 구문을 찍은 것입니다.


cmp cl, ?? 로 한글자씩 비교하는 구문이 일치합니다.


한 번 저런 형식의 명령어만 뽑도록 만들어봅시다.


objdump -d -M intel ./sorcery_dist/[파일이름] | grep 'cmp   .cl'


이러면 cmp   .cl, ?? 형태의 명령어만 뽑아줍니다.



하지만 안타깝께도 플래그를 전부 긁어내지 않더군요..

(왜냐하면 cmp    al,?? 형식의 구문이 있는 것들도 몇몇 있으니까요)


이런형식의 명령어를 써볼까요?


objdump -d -M intel ./sorcery_dist/[파일이름] | grep 'cmp    .l'



숫자를 파싱할 수 있게되었으니까.... 필요없는 정보들을 덜어내야겠죠?


특징을 살펴보니 플래그는 printable하고 플래그가 아닌 것들은 대부분 printable하지 않습니다.


그렇다면 printable 한것들만 흘려내고 printable하지 않은 것들부터는 싹 걸러내면 되겠네요.


python으로 코드를 한 번 만들어내봅시다.



실행시켜주시면 이런 결과가 뜹니다.



빡시네요. ㅎㅎ


ls 명령어 이용해서 파일 이름 긁으셔서 fileList 만들면 됩니당.

리다이렉션 이름은 왜 다르게 해줬냐면 cat a > a 이런식으로 리다이렉션하면 결과가 비더라구요;;


이상 소서리 풀이였습니다.


Comments