2021.01.04


1. W3Challs - First steps on Windows (Reversing, 3pts)


[그림 1] 프로그램 실행 화면


이 프로그램을 실행하면 위 사진과 같이 문자열을 입력할 수 있다. 조건을 만족하는 특정 문자열을 입력하면 flag가 출력되는 형태의 프로그램인 듯 하다.

Ghidra를 사용하여 해당 프로그램을 분석해보자.

우선 Defined Strings를 통해 Main 함수를 찾아보기로 했다. 위 사진에 출력된 문자열 중 ‘Invalid password’를 검색했더니 FUN_401d70 함수 내 포함된 문자열로 나온다. 그 외에도 이런저런 문자열들이 포함된 것으로 보아 여기가 메인임이 확실하다.

이제 메인을 찾았으니 분석하는 일만 남았다.

[그림 2] Main 함수 중 일부


위 사진의 코드가 입력한 문자열을 검증하는 부분인 듯 하다.

입력값과 임의의 데이터 2개를 XOR 연산을 총 0x21(33)번 반복한다. 이 때 XOR 연산의 결과는 0이 되어야 한다. 즉, 입력값[i] ^ XOR_arr[i] ^ XOR_arr_2[i] == 0이 되어야 한다는 것이다.

그리고, 올바른 입력값을 넣었을 때 ‘use it as the flag’라는 글이 출력되는 것으로 보아 입력값이 플래그가 되는 듯 하다.

그렇다면 입력값과 XOR을 하는 데이터들을 알면 바로 플래그를 확인할 수 있을 것이다.

[그림 3] XOR 연산 데이터셋


해당 데이터들 역시 기드라에서 확인할 수 있다. 다만 기드라에서 추출하는 건 좀 귀찮아서, 나는 HxD를 이용해서 가져왔다. 각각 34바이트씩 가져오면 된다. 입력값의 총 길이는 마지막 NULL값을 포함해 총 34바이트이기 때문!

어쨌든 데이터를 추출했으면 간단한 코드를 짜서 플래그를 얻을 수 있다. 밑의 코드는 내가 만든 python 코드다. 부족하지만 참고에 도움이 됐으면 좋겠다.

import string

array1 = [0xEE, 0x81, 0x29, 0x75, 0x9C, 0x97, 0x16, 0xF4, 0xDA, 0x03, 0xDE,
          0xF8, 0xD5, 0xF8, 0xD6, 0x19, 0x0F, 0x6F, 0x4C, 0x15, 0xAC, 0x59,
          0xCF, 0x85, 0xA6, 0xB0, 0x25, 0x1B, 0x88, 0xA7, 0xA8, 0xD1, 0x06, 0x00]

array2 = [0xB9, 0xB2, 0x6A, 0x0E, 0xCB, 0xA4, 0x7A, 0x97, 0xEA, 0x6E, 0xED,
          0xD8, 0x82, 0xC9, 0xE1, 0x71, 0x2F, 0x38, 0x7D, 0x7B, 0xC8, 0x69,
          0xB8, 0xB0, 0x86, 0xF3, 0x57, 0x2F, 0xEB, 0xCC, 0xC5, 0xE2, 0x7B, 0x00]

i = 0
flag = ''

while (i < 0x21):
    for a in string.printable:
        if ord(a) ^ array1[i] ^ array2[i] == 0:
            flag += a
            i += 1

print(flag)

해당 코드를 실행하면 플래그를 바로 얻을 수 있다. w3challs에서 가서 입력하면 3점을 얻을 수 있다!

[그림 4] Flag


[그림 5] 풀이 완료!


여담으로 이 프로그램에는 디버깅 탐지 루틴이 달려있어서 디버거로 동적 분석하려면 조금 귀찮아진다. 제일 쉬운 문제인데 왜 달려있지…

2. W3Challs - ProtectionSystem Ⅰ (Reversing, 3pts)


[그림 1] 프로그램 실행 화면


이 프로그램도 위 문제와 마찬가지로 문자열 입력값을 받고 검증을 한 후 결과를 출력해주는 형식을 갖고 있다. 빠르게 Ghidra에 넣고 Defined Strings를 이용해 Main 함수를 찾자. 프로그램에 출력되었던 “Hit a key to quit.” 문자열이 FUN_00410940 함수에 포함된 것을 확인하였다. 대충 훑어보니 여기가 메인이 맞는듯 하여 바로 분석에 들어갔다.

함수가 꽤 긴 편인데 문제를 푸는 데 있어서는 위 사진에 나타난 부분만 볼 수 있으면 된다. 프로그램에 출력되기도 했던 “W3challs Protection system”, “Please enter your password :” 문자열 중 일부를 가져와 총 9바이트의 문자열을 만드는데, 문자를 가져올 때 그냥 넣는 것이 아니고 추가로 임의의 수만큼 더하고 빼고 곱하는 등의 연산을 하는 것도 볼 수 있다.

아무튼 그렇게 문자열을 만든 뒤 strcmp() 함수를 통해 입력값과 해당 문자열을 비교하는 것까지 확인할 수 있다.

이 문제를 푸는 방법은 크게 두 가지로 나눌 수 있다. 첫번째는 문자열 생성 루틴을 구현해서 플래그를 얻는 것이고, 두번째는 strcmp() 함수에 Breakpoint를 걸어서 함수 인자에 걸려있을 플래그를 확인하는 것이다.

첫번째 방법으로 푸는 것은 아래와 같은 코드를 작성하면 된다. (코드가 다소 깔끔하지 못한 점 양해바람…)

import string

str_w3 = "W3challs Protection system"
str_pw = "Please enter your password :"

dat_0 = ord(str_w3[13]) - 3
dat_1 = ord(str_pw[14]) - 6
dat_2 = ord(str_w3[0]) - 0xC
dat_3 = ord(str_pw[1]) + 5
dat_4 = ord(str_w3[13]) - 9 + 3
dat_5 = ord(str_pw[14]) - 9 + 6
dat_6 = ord(str_w3[1]) - 8 + 9
dat_7 = ord(str_pw[1]) - 9 + 4
dat_8 = ord(str_w3[4]) + 9 * 2 + 7

flag = [chr(dat_0), chr(dat_1), chr(dat_2), chr(dat_3), chr(dat_4),
        chr(dat_5), chr(dat_6), chr(dat_7), chr(dat_8)]

print(''.join(flag))

두번째 방법은 디버거를 이용해서 strcmp() 함수를 호출하는 곳에서 bp를 걸고 인자를 확인하면 된다.

[그림 2] strcmp 인자 확인

위 그림에서 까만 선으로 칠한 부분이 플래그값이다. 코딩할 것도 없이 바로 보여준다.

[그림 3] 풀이 완료!

어쨌든 플래그를 구한 뒤 W3Challs에 가서 해당 값을 그대로 입력해주면 9점을 얻을 수 있다! 나처럼 괜히 위 문제의 플래그마냥 W3C{}로 플래그를 감싸갖고 내서 삽질하는 사람이 없길 바란다..