Dream Hack write-up(2)

BigTree·2023년 4월 6일
0
post-thumbnail

벌써 한 주가 지나서 새로운 숙제를 제출해야 할 때가 다가오고 있다. 저번에 제출 2시간 전부터 했더니 너무... 급하게 문제를 풀어야했어서 이번에는 이틀 전부터 해보려고 한다!

저번에는 Crypto를 주로 풀었는데, 이번에는 새롭게 Reversing에 도전해보자. 먼저 Dream Hack에서 제공하는 리버싱 로드맵으로 공부하고 basic 문제 3개를 풀어 보는 것이 오늘의 계획이다!1!!

Dream hack : 리버싱 로드맵
https://dreamhack.io/lecture/roadmaps/4

역공학/리버스 엔지니어링/리버싱은 모두 같은 말입니다. 드림핵의 로드맵에 따르면 "완성된 제품을 해체하고 분석하여 구조와 기능, 디자인을 파악"하는 것이라고 한다. 자세한 설명들은 위의 로드맵 링크를 통해 알아보시길!

리버싱은.. 참 아는게 많아야하는 CTF 문제인것 같다. 고급언어 알아야하고, 컴파일 과정 및 방법 당연히 알아야하고, 어셈블리어도 읽고 해석할 줄 알아야하고 문제에 맞는 툴도 사용할 줄 알아야한다... 거기에 파일구조와 프로그램 구조까지 알아야한다. 그러니 전공 수업에서 컴퓨터 구조를 배운 후에 리버싱 문제를 푸는 것이 더 수월할 것이다. 안푸는게 제일 정신 건강에 좋다 암호에 이어.. 이런것만 건드리는 나도 제정신은 아닌듯...

아무튼... 서론이 길었다. 문제를 풀어보자

rev_basic_0

🍇LEVEL 1 : Reversing

문제
이 문제는 사용자에게 문자열 입력을 받아 정해진 방법으로 입력값을 검증하여 correct 또는 wrong을 출력하는 프로그램이 주어집니다.
해당 바이너리를 분석하여 correct를 출력하는 입력값을 찾으세요!
획득한 입력값은 DH{} 포맷에 넣어서 인증해주세요.

문제파일을 다운로드 한 후, 실행하면 input 값을 요구하고 input 값에 따라 Wrong/correct가 출력된다.

PS C:\Users\user\Downloads\rev-basic-0> .\chall0.exe
Input : AAAA
Wrong

물론 brute force 방식으로 정답을 알아내는 것도 가능하겠지만, 일단은 리버싱 문제이니 정적분석을 시도해보자.

IDA를 사용하면 파일을 분석하는 데 도움을 받을 수 있다.
아래는 IDA로 문제 파일을 열었을 때 볼 수 있는 화면이다.

우리는 현재 프로그램에서 wrong/correct라는 단어를 출력하는 것을 알고 있기 때문에, string 검색을 통해 해당 부분으로 이동해본다. Shift-F12 단축키를 사용하면 stirng view로 이동할 수 있다.
키워드 검색을 통해 wrong을 찾았다.

해당 주소로 이동했을 때 볼 수 있는 코드

wrong을 선택한 후, x를 눌러서 해당 함수의 main을 찾고 다시 f5를 눌러서 디컴파일을 하면! 이런 결과를 얻을 수 있다. 그냥 한 번에 디컴파일까지 해줬으면 좋겠다

코드를 보면, sub_140001190에서 "Input : "을 출력하고, sub_1400011F0에서 그 값을 입력받는 것을 알 수 있다. 그 아래 if에서 v4에 저장된 값을 sub_140001000에 전달해주고 그 값의 반환에 따라 Correct와 Wrong을 출력하는 것으로 보아, sub_140001000에서 입력한 값을 flag와 비교하고있다는 것을 알 수 있다.

sub_140001000 함수를 확인하면 Compar3_the_str1ng의 값을 입력받은 값과 비교하여 그 결과를 반환하고 있는 것을 확인할 수 있습니다.

따라서! flag는 DH{Compar3_the_str1ng}입니다.

rev_basic_1

🍇LEVEL 1 : Reversing

문제
Reversing Basic Challenge #1
이 문제는 사용자에게 문자열 입력을 받아 정해진 방법으로 입력값을 검증하여 correct 또는 wrong을 출력하는 프로그램이 주어집니다.
해당 바이너리를 분석하여 correct를 출력하는 입력값을 알아내세요.
획득한 입력값은 DH{} 포맷에 넣어서 인증해주세요.
예시) 입력 값이 Apple_Banana일 경우 flag는 DH{Apple_Banana}

rev_basic_0와 같은 과정을 거쳐서 main 함수를 compiling하면 아래와 같은 결과를 얻을 수 있다.

마찬가지로 sub_140001000 함수를 확인하면 flag를 얻을 수 있다.

_BOOL8 __fastcall sub_140001000(_BYTE a1)
{
if (
a1 != 67 )
return 0i64;
if ( a1[1] != 111 )
return 0i64;
if ( a1[2] != 109 )
return 0i64;
if ( a1[3] != 112 )
return 0i64;
if ( a1[4] != 97 )
return 0i64;
if ( a1[5] != 114 )
return 0i64;
if ( a1[6] != 51 )
return 0i64;
if ( a1[7] != 95 )
return 0i64;
if ( a1[8] != 116 )
return 0i64;
if ( a1[9] != 104 )
return 0i64;
if ( a1[10] != 101 )
return 0i64;
if ( a1[11] != 95 )
return 0i64;
if ( a1[12] != 99 )
return 0i64;
if ( a1[13] != 104 )
return 0i64;
if ( a1[14] != 52 )
return 0i64;
if ( a1[15] != 114 )
return 0i64;
if ( a1[16] != 97 )
return 0i64;
if ( a1[17] != 99 )
return 0i64;
if ( a1[18] != 116 )
return 0i64;
if ( a1[19] != 51 )
return 0i64;
if ( a1[20] == 114 )
return a1[21] == 0;
return 0i64;
}

숫자를 아스키 코드로 바꾸면 flag를 얻을 수 있다.

BOOL8 __fastcall sub_140001000(_BYTE a1)
{
if (
a1 != 'C' )
return 0i64;
if ( a1[1] != 'o' )
return 0i64;
if ( a1[2] != 'm' )
return 0i64;
if ( a1[3] != 'p' )
return 0i64;
if ( a1[4] != 'a' )
return 0i64;
if ( a1[5] != 'r' )
return 0i64;
if ( a1[6] != '3' )
return 0i64;
if ( a1[7] != '
' )
return 0i64;
if ( a1[8] != 't' )
return 0i64;
if ( a1[9] != 'h' )
return 0i64;
if ( a1[10] != 'e' )
return 0i64;
if ( a1[11] != '_' )
return 0i64;
if ( a1[12] != 'c' )
return 0i64;
if ( a1[13] != 'h' )
return 0i64;
if ( a1[14] != '4' )
return 0i64;
if ( a1[15] != 'r' )
return 0i64;
if ( a1[16] != 'a' )
return 0i64;
if ( a1[17] != 'c' )
return 0i64;
if ( a1[18] != 't' )
return 0i64;
if ( a1[19] != '3' )
return 0i64;
if ( a1[20] == 'r' )
return a1[21] == 0;
return 0i64;
}

flag는 DH{Compar3_the_ch4ract3r}

patch

🍇LEVEL 1 : Reversing

문제
flag를 그리는 루틴을 분석하고 가려진 flag를 보이게 해주세요.
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
GDI+ - Win32 apps | Microsoft Docs
Graphics Functions - Win32 apps | Microsoft Docs
File — x64dbg documentation

문제에서 주어지는 파일을 다운로드 받은 후 실행하면 아래처럼 flag가 가려진 프로그램이 실행된다.

프로그램을 자체적으로 patch해서 flag를 가리는 부분을 제거해야한다.
GUI 프로그램의 경우 WinMain 함수를 찾아야한다. IDA의 import 탭에서 보편적으로 윈도우 창을 만들 때 사용하는 CreateWindowExW 함수를 이용하여 WinMain 함수를 찾고, WinMain 함수를 이용하여 decompiling을 진행한다.

14번째 줄에서 lpfnWndProc가 메시지 처리 콜벡 함수를 등록한다. 윈도우를 생성하고 메시지를 처리할 때 여기에 등록한 함수를 사용하여 처리한다.
여기에서 사용하고 있는 sub_1400032F0 함수를 들여다보면, BeginPaint와 EndPaint를 지정하고 있고, 중간에 sub_14002C40을 호출하고 있다. 즉, sub_14002C40 함수에서 Flag를 비롯한 무언가를 그리고 있다고 짐작할 수 있다.

sub_14002C40 함수를 보면 반복적으로 sub_140002B80 함수를 호출하는데, 해당 함수는 펜을 생성하고, 라인 1개를 그리고 펜을 제거하는 기능을한다. 따라서 이 함수가 FLAG를 그리는 함수라고 생각할 수 있다.

이와 다르게 sub_1400017A0 함수를 살펴보면 반복적으로 펜을 생성하고 line을 그리고 있다. 따라서 함수가 Flag를 그리는 함수라고 생각할 수 있다.

따라서 flag를 지우는 sub_140002B80 함수가 동작하지 않도록 IDA View-A 페이지에서 sub_140002B80함수를 찾은 후, Edit>Patch Program > Assemble을 이용하여 프로그램을 패치한다. Assemble에서 함수가 동작하지 않도록 ret를 삽입한다.

Edit>Patch Progrm>Apply Patchs to input file을 실행하면 flag가 가려지지 않은 프로그램이 만들어진다.
프로그램을 다시 실행하면 flag를 얻을 수 있다.

flag는 DH{UPATCHED}

0개의 댓글