MalwareTech.com 리버싱 챌린지 풀이

dusty
11 min readMar 4, 2022

malwaretech.com의 windows-reversing 챌린지는 정적 분석만으로 플래그를 획득하는, 쉬운 난이도의 챌린지 7문제로 구성되어 있습니다. 이번 글에서는 7문제를 모두 풀이하면서 어떤 방식으로 풀었는지 간단하게 정리해보았습니다. 챌린지는 strings, shellcode, vm, ransomware 4개의 영역으로 구성되어 있으며 각각 3개, 2개, 1개, 1개의 문제가 제공됩니다.

strings

strings1 — [★☆☆☆☆]

strings1.exe contains an un-encrypted flag stored within the executable. When run, the program will output an MD5 hash of the flag but not the original. Can you extract the flag?

실행 파일 내부에 암호화되지 않은 플래그가 존재하고, 프로그램을 실행할 경우 플래그의 MD5 해시를 출력합니다. 디버거나 덤퍼 등을 이용한 동적 분석 없이 정적 분석 도구만 사용하여 플래그를 얻는 것이 문제 풀이의 목표입니다.

1. strings1 문제를 IDA로 열어, 파일 내 문자열을 확인한 화면

바이너리 내에 여러 개의 문자열이 존재하지만, 실제로 사용되는 문자열은 단 하나뿐입니다.

2. strings1 문제를 IDA로 처음 열었을 때의 화면

각각 문자열의 xref를 확인할 필요 없이, 바이너리의 start() 함수에서 해시 함수의 인자로 사용하는 문자열을 찾으면 플래그를 확인할 수 있습니다.

strings2 — [★☆☆☆☆]

strings2.exe contains an un-encrypted flag stored within the executable. When run, the program will output an MD5 hash of the flag but not the original. Can you extract the flag?

strings1 문제와 동일하게, 바이너리 내에 암호화되지 않은 플래그가 존재하고, 이를 얻으면 되는 문제입니다.

3. strings2 바이너리의 start() 함수를 IDA를 이용해 디컴파일한 결과

strings2 바이너리의 start() 함수는 스택을 이용하여 문자열을 해시 함수에 전달하고 있고, 이를 플래그로 제출하면 정답입니다.

strings3 — [★★☆☆☆]

strings3.exe contains an un-encrypted flag stored within the executable. When run, the program will output an MD5 hash of the flag but not the original. Can you extract the flag?

strings1, 2 문제와 동일하게 바이너리 내에 암호화되지 않은 플래그가 존재하고, 이를 획득해서 제출하면 되는 문제입니다.

4. strings3 바이너리를 IDA를 이용해 디컴파일한 화면

주어진 바이너리의 start() 함수를 리버싱해보면, uID = 272인 리소스를 LoadStringA 함수를 이용해 버퍼에 불러오는 것을 확인할 수 있습니다. Resource Hacker 등 PE 바이너리의 리소스 섹션을 확인할 수 있는 툴을 사용해 플래그를 획득할 수 있습니다.

5. Resource Hacker를 이용해 uID=272인 플래그를 확인하는 화면

shellcode

shellcode1 — [★☆☆☆☆]

shellcode1.exe contains a flag stored within the executable. When run, the program will output an MD5 hash of the flag but not the original. Can you extract the flag?

shellcode1 바이너리 내에 난독화된 문자열과 이를 복호화하는 쉘코드가 존재하고, 리버싱을 통해 플래그를 복호화하여 획득하면 되는 문제입니다.

6. shellcode1 바이너리를 IDA를 이용해 디컴파일한 화면

data 섹션의 0x404068 오프셋에 위치하는 13 바이트의 쉘코드를 읽어, 이를 실행하는 것을 확인할 수 있습니다.

7. data 섹션의 0x404068 위치의 쉘코드

IDA에서 c 키를 눌러, 해당 오프셋의 내용을 코드로 전환한 후의 화면은 위와 같습니다. start() 함수의 내용과 위 쉘코드를 통해, 0x404040 위치의 ASCII 스트링을 바이트 단위로 5번 rol 하는 코드임을 알 수 있습니다.

0x404040 위치의 스트링은 32620a3adb9a422a62621a7a222a694a9a72a26952aa9aa269327a92692ac282627a4aa29aeb0000 이고, 이를 쉘코드와 동일하게 복호화하는 파이썬 코드를 간단하게 작성해보았습니다.

8. rol.py 스크립트의 실행 결과

해당 스크립트를 실행하면, 복호화된 플래그를 획득할 수 있습니다.

shellcode2 — [★★☆☆☆]

shellcode2.exe contains a flag stored within the executable. When run, the program will output an MD5 hash of the flag but not the original. Can you extract the flag?

strings2 문제와 유사하게, start() 함수의 스택에 플래그가 존재하고, 바이너리는 이를 복호화한 다음 해시 함수의 결과를 출력하는 형태입니다.

9. shellcode2의 start() 함수에서 스택에 복호화된 문자열을 저장하는 화면
10. shellcode2의 start() 함수에서 sub_404040 함수를 호출해 플래그를 복호화하는 부분

start() 함수 내에서 sub_404040 함수를 통해 플래그의 복호화를 진행하고 있습니다.

11. sub_404040 함수의 복호화 루틴

sub_404040 함수는 LoadLibraryA 함수를 이용해, 필요한 함수를 동적으로 로드하여 사용하는 형태를 띄고 있습니니다. 해당 함수가 복호화하는 루틴을 파이썬 스크립트로 구현한 코드는 아래와 같습니다.

해당 스크립트를 실행하면, 복호화된 플래그를 획득할 수 있습니다.

12. shellcode2의 플래그 복호화 스크립트의 실행 결과 화면

De-virtualization

vm1 — [★★★☆☆]

vm1.exe implements a simple 8-bit virtual machine (VM) to try and stop reverse engineers from retrieving the flag. The VM’s RAM contains the encrypted flag and some bytecode to decrypt it. Can you figure out how the VM works and write your own to decrypt the flag? A copy of the VM’s RAM has been provided in ram.bin (this data is identical to the ram content of the malware’s VM before execution and contains both the custom assembly code and encrypted flag).

해당 문제는 간단한 8-bit VM을 구현한 바이너리와, 암호화된 플래그를 담고 있는 VM의 램을 파일로 제공합니다. 바이너리를 분석하여 VM이 동작하는 방식을 알아내고, 플래그를 복호화하는 스크립트를 작성하여 플래그를 획득할 수 있습니다.

13. vm1 바이너리의 start() 함수

주어진 바이너리의 start() 함수는 unk_404040 위치의 데이터를 507 바이트만큼 버퍼에 복사한 다음, sub_4022E0 함수를 실행하도록 구현되어 있습니다.

14. sub_4022E0 함수를 디컴파일한 화면

sub_4022E0 함수에서는 sub_402270 함수의 리턴 값이 1이 아닐때까지 해당 함수의 실행을 반복하고 있습니다. sub_402270 함수의 인자로는 버퍼의 255 + 0번째 바이트부터 255 + 2번째 바이트까지 총 3개의 인자가 전달되는 것을 확인할 수 있습니다.

15. sub_402270 함수를 디컴파일한 화면

해당 함수에서는 첫 번째로 전달된 인자의 값에 따라, 버퍼의 값이나 VM의 레지스터를 조작하는 동작을 하고 있습니다. 파이썬 코드로 동일한 동작을 하는 코드를 작성하여, 주어진 ram.bin의 데이터를 통해 VM을 실행하면 복호화된 플래그를 획득할 수 있습니다.

16. run.py 스크립트를 실행한 화면

Ransomware

ransomware1 — [★☆☆☆☆]

The administrator for FlagCorp was using an outdated Windows 7 system and got infected with some ransomware. We believe this variant was most likely written by a scriptkiddie due to the fact it was so badly designed it encrypted itself. One of our malware analysts was able to recover the encryption function from memory but doesn’t know much about cryptography. Can you find a way to decrypt flag.txt?

이번 문제에는 암호화 루틴이 담긴 바이너리와 암호화된 플래그, Sample Pictures라는 디렉토리 내의 암호화된 이미지 여러 장이 제공됩니다.

17. ransomware1 바이너리 내의 암호화 루틴

v4[i] ^= *(_BYTE*)(a2 + i % 0x20); 을 통해 XOR에 사용된 키는 32 바이트라고 추측할 수 있습니다. 하지만 바이너리의 start() 함수에서 a2 = 0이 전달되므로, 실제로 암호화에 사용된 키를 바이너리 분석을 통해 알 수 없습니다.

문제와 같이 제공된 암호화된 Sample Pictures는 Windows 7의 샘플 이미지로 제공되던 이미지로, archive.org에서 원본 파일을 다운로드 받을 수 있습니다. 필자는 Chrysanthemum.jpg를 다운로드 받아, 암호화된 파일과 XOR하여 XOR 키를 얻어냈습니다. 사용한 파이썬 스크립트는 아래와 같습니다.

18. decrypt.py 스크립트의 실행 결과

--

--