본문 바로가기

2. Reversing (리버싱)/1) Write UP

[2020.11.14]CodeEngn Challenges Basic RCE L17, L18 라이트업

L17. Key 값이 BEDA-2F56-BC4F4368-8A71-870B 일때 Name은 무엇인가

힌트: Name은 한자리인데.. 알파벳일수도 있고 숫자일수도 있고..

정답인증은 Name의 MD5 해쉬값(대문자)

 

우선 프로그램을 실행해 Name에는 1을, Key에는 BEDA-2F56-BC4F4368-8A71-870B를 넣고 실행하니 글자 수를 더 입력하라는 메시지가 출력되었다.

그러나 문제에서 Name은 한 글자라고 했기 때문에 글자수를 확인하는 조건의 수정이 필요하였다. Key에 입력된 메시지 근처에서 글자수를 비교하는 CMP를 찾아보았다.

출력문 근처에서 입력된 값의 글자수와 3을 비교하는 CMP가 있었다. 문제 해결을 위해 비교하는 숫자를 3에서 1로 변경해주고 다시 값을 입력해보았다.

 

한 줄씩 코드를 실행해보며 값을 살피던 중, 성공 구문을 출력하는 명령문 근처에서 Key 값이 변형되는 구문을 찾을 수 있었다.

해당 함수 0045B850 내부로 들어가 코드를 한 줄씩 실행시켜보았다.

 

함수 실행 중 45B898 부근에 몇 가지 연산이 있었는데, 연산 후에 EDX에는 63E2EBF0가 저장되어 있었다.

 

함수가 종료되니 Key 값이 아래와 같이 변환된 것을 확인할 수 있었다. 반환된 Key 값의 앞 부분이 위의 EDX에 저장된 값의 앞 4자리와 같았다. 해당 함수의 연산 반복을 통해 입력된 Key 값이 변형된다는 것을 확인하였다. 값이 변환되는 연산이 어떻게 진행되는지 자세히 살펴보았다.

 

변형 과정은 아래의 연산을 거친다.

MOV ECX, 1

ECX = 1

MOV EBX, DWORD PTR SS:[EBP-4]

스택의 EBP- 주소의 4바이트 공간의 값을 EBX에 대입 / EBX = 31(‘1’)

MOVZX ESI, BYTE PTR DS:[EBX+ECX-1]

ESI에 입력한 Name값 대입 / ESI = 31

ADD ESI, EDX

ESI = ESI + EDX = 31

IMUL ESI, ESI, 772

ESI = ESI * 0x772 = 00016CD2

MOV EDX, ESI

EDX = ESI = 0016CD2

IMUL EDX, ESI

EDX = EDX * ESI = 07E5DC44

ADD ESI, EDX

ESI = ESI + EDX = 07E74916

OR ESI, ESI

ESI = ESI | ESI  = 07E74916

IMUL ESI, ESI, 474

ESI = ESI * 0x474 = 31F175F8

ADD ESI, ESI

ESI = ESI + ESI = 63E2EBF0

MOV EDX, ESI

EDX = ESI = 63E2EBF0

INC ECX

ECX = ECX + 1 = 2

DEC EAX

EAX = EAX – 1 = 0

(MOVZX A, B: B를 A로 복사한 후, 나머지 비트를 0으로 채움

IMUL A, B: 음수를 포함한 부호 있는 정수 A, B를 곱하는 연산)

 

해당 연산을 통해 입력된 Name의 값이 변형이 되어 입력한 Key 값과 비교된다는 것을 알아내었다. 문제에서 제공한 힌트는 Name은 한 자리의 알파벳 또는 숫자가 될 수도 있다는 것이었다. C언어로 해당 연산을 통해 BEDA로 시작하는 문자가 무엇인지 찾아보는 프로그램을 작성하였다.

프로그램 실행 결과, F의 변환 값이 beda로 시작한다는 것을 찾아내었다. 문제의 정답은 정답의 대문자 MD5 해쉬값이므로 F의 MD5 해쉬값을 구해 정답으로 제출하였다. 아래는 알파벳 F의 MD5 해쉬값이다.

성공메시지가 출력된다.




L18. Name이 CodeEngn일때 Serial은 무엇인가

 

 Name으로 CodeEngn을 입력하고 시리얼 값으로 123이라는 임의의 값을 입력하면 실패 메시지가 출력된다.

 실패문자열의 위치로 이동하면 그 위에 성공 문자열과 분기하는 JE 명령과 문자열 비교함수인 IstrcmpiA를 볼 수 있다.

 비교 함수를 호출하는 부분에 BP를 설정하고 Name에 CodeEngn, 임의의 시리얼 값을 넣어 실행시킨다. 그러면 String2에 입력된 Name을 통해 연산한 시리얼키가 저장된다. String2의 값을 시리얼 값으로 넣어본다.

성공 메시지가 출력된다.