틀린 부분이 있다면 언제든지 댓글 남겨주세요!
Malwarebytes-crackme 1
이 파일은 Malwarebytes사에서 제공하는 훈련용 악성코드이다.
9가지의 디버거 탐지 코드를 거쳐 process hollowing을 통해 rundll32.exe프로세스에 인젝션 코드를 주입한 후 실행시키면 성공 문자열을 확인할 수 있다.
정적 분석 (Exeinfo, Bintext)
우선 정적 분석을 통해 파일의 형식과 사용되는 API, 문자열 등을 확인해보았다.
상세 분석(x64dbg, process explorer)
x64dbg으로 동적 분석을 통해 상세 분석을 진행한다.
파일을 실행시켰을 때 나왔던 문자열을 이용해 핵심 코드를 찾아간다.
'I am so sorry, you failed! :( ' 문자열을 이용해 해당 코드를 찾아왔다.
4014F0 함수가 9가지 디버거를 탐지하는 부분이다. 이 함수에서 디버거 탐지를 하며 flag 값을 완성시킨다.
해당 flag 값이 형성된 경우 복호화를 통해 url 주소를 찾아낸다.
성공적으로 복호화가 완료되면 4019A5 위치로 분기되어 401690 함수로 이동한다.
401690 함수 내에서 위에서 뽑아온 url에 접속하여 인코딩(base64)된 페이로드를 다운로드 하여 가져온다.
이 데이터를 디코딩하고 압축해제 하고 ClipBoard에서 가져온 키 값과 XOR하여 복호화한다.
이후 프로세스 할로잉 기법을 사용하여 rundll32.exe 프로세스에 디코딩 된 페이로드를 삽입한다.
* 프로세스 할로잉(Process Hollowing): 메모리 주입 공격의 일종으로, 시스템 프로세스를 일시정지 상태(Suspend)로 실행시킨 다음 해당 프로세스에 페이로드를 주입시켜 원하는 행위를 수행시키는 방법
전체적인 흐름을 보았으니 좀 더 자세히 들어가보도록 한다.
우선 4014F0 함수 내 9가지 디버깅 방법이다.
추후 뒷 과정을 진행할 때 flag 생성이 원활하게 진행되고 이 부분을 반복하지 않게 하기 위해 디버거 탐지 부분마다 코드 패치를 진행할 것이다.
1. IsDebuggerPresent API (디버깅 중이면 eax=1), CheckRemoteDebuggerPresent API (디버깅 중이면 esi=1)
디버거에서 실행할 경우 IsDebuggerPresent API를 거치면 eax=1를 리턴하여 JE분기되지 않고 flag 생성 루틴을 진행한다. 디버거 여부와 상관없이 항상 flag 생성 루틴으로 들어갈 수 있도록 분기문을 NOP코드로 패치한 후 계속 진행한다.
2. RaiseException API (예외가 발생하면 첫번째 매개변수로 넘겨진 주소로 이동), SEH handler 설정 (FS:[0])
디버거가 수행중이면 예외 발생 코드로 이동하도록 되어 있다.
1번과 마찬가지로 401AB3(flag값 생성 루틴)이 항상 실행되도록 JE 분기문을 패치한다. (74 2E > 90 90)
3. Hardware breakpoint 탐지
GetCurrentThreadId API를 이용해 thread handle을 열고, handle을 이용하여 openthread API로 주소를 전달하여GetThreadContext API로 해당 thread의 context를 가져온다.
이 context 내에 존재하는 hardware breakpoint 설정값을 확인하여 디버거 환경인지 탐지할 수 있다.
현재 설정해 놓은 hardware BP가 없기 때문에 모두 0 이지만 있는 경우 값이 존재하며, 있는 경우 디버거가 존재한다고 판단할 수 있다.
마찬가지로 JE 분기문을수정하여 패치한다.
4. PEB 구조체 멤버 이용
FS:[30] 주소로 PEB구조체에 접근한다. 구조체 중 [PEB+0x68]은 NtGlobalFlags값 (0x70=디버거 실행중, 0x00=정상)이다.
디버깅 중이면 JE코드에서 분기되지 않아 flag 값 생성 코드가 수행된다.
마찬가지로 디버깅 여부와 상관없이 실행되도록 이 부분을 NOP으로 패치한다.
5. 문자열 해시값과 QueryDosDevice API(현재 장치 이름 검색) 이용
가상머신 장치이름을 확인하여 해당 API의 반환값이 0이 아닌 경우 디버거 환경으로 판단한다.
기존에 가상머신에 해당하는 장치 이름의 해시값과 현재 환경의 장치 이름의 해시값을 비교하는 방식이다.
마찬가지로 패치 후 계속 진행한다.
6. RegOpenKey
“HARDWARE\\ACPI\\DSDT\\VBOX_”문자열과 같은 레지스트리 키 정보가 있으면 0이 아닌 값을 반환한다.(800000002=HKEY_LOCAL_MACHINE)
디버거 환경이라면 RegOpenKeyA API 반환값이 2이다.
마찬가지로 패치 후 계속 진행한다.
7. 가상머신 및 디버거 관련 모듈 확인
CreateToolhelp32Snapshot, Module32First, Module32Next API 이용하여 프로세스 로드된 모듈 정보를 불러와 가상환경 관련 모듈이 있는지 확인한다. 비교 대상 문자열은 5번의 방식과 같이 해시값을 이용한다. (401EB0 = 해시처리 함수)
마찬가지로 402D71 코드를 패치 후 계속 진행한다.
8. 가상머신 및 디버거 관련 프로세스 확인
CreateToolhelp32Snapshot, Process32First, Process32Next API 이용하여 가상환경을 나타내는 프로세스 BoxService.exe, VBoxTray.exe와 같은 프로세스가 있는지 확인한다. (7번과 비슷)
402F7E-402F7F를 패치한 상태이다.
9. 시스템 시간차 이용
이제 본격적으로 페이로드를 다운받아 process hollowing을 진행한다.
생성된 flag count 와 flag 데이터를 이용해 복호화 하여 url을 추출한다.
401690 함수 내에서 위에서 뽑아온 url에 접속, 페이로드 다운, 인코딩(base64)된 페이로드 디코딩, 프로세스 할로잉 기법을 사용하여 rundll32.exe 프로세스에 디코딩 된 데이터 삽입이 진행된다.
우선 url 접속 부분이다.
인터넷 연결상태를 확인하고 할당까지 성공한 후 분기된 부분에서 본격적인 인터넷 연결 후 위에서 구한 url로 접속하여 파일을 다운받는다.
url에 접속하여 데이터를 성공적으로 읽으면, 읽은 데이터를 heap영역을 생성해 복사한다.
해당 데이터를 디코딩 하는 함수에서 4byte씩 연산 후, 새로 생성한 heap에 저장한다.
이후 디코딩 한 데이터의 압축을 해제한다. 이때 RtlDecompressBuffer API를 이용한다.
RtlDecompressBuffer함수 실행 시 반환값은 STATUS_SUCCESS (0) 이어야 4059B5로 분기된다.
(그 외에 가능한 반환값은 STATUS_UNSUPPORTED_COMPRESSION (C0000242), STATUS_INVALID_PARAMETER (C000000D)가 있다.)
RtlDecompressBuffer함수 실행 시 반환값은 STATUS_SUCCESS (0) 이어야 4059B5로 분기된다. 그 외에 가능한 반환값은 STATUS_UNSUPPORTED_COMPRESSION (C0000242), STATUS_INVALID_PARAMETER (C000000D)가 있다.
디코딩 한 문자열에 키 값이 포함되어 있다. 이 키 값을 클립보드에 복사하여 암호화 키 값으로 사용한다.
406820 함수에서 Clipboard 관련 API를 이용하여 클립보드의 데이터를 읽어온다. (= malwarebytes > ctrl+C )
4011A0 함수에서 키 값(malwarebytes)를 이용하여 데이터를 복호화 한 결과 아래와 같이 PE구조를 가지는 데이터임을 확인할 수 있다.
MZ 시그니처 확인 등을 통해 PE 구조를 확인한 후 프로세스를 생성한다.
process explorer를 통해 suspend상태로 rundll32.exe 프로세스가 실행되는 것을 확인할 수 있다.
앞에서 복호화한 페이로드를 쉘코드로 rundll32.exe프로세스에 쉘코드로 입력하기 위해 PE구조를 확인하고, 프로세스와 스레드의 핸들 값을 가져와 읽기쓰기실행 권한 허용을 해준다.
이후 heap영역을 할당해 쉘코드로 PE header, section데이터 모두 삽입한다.
VirtualAlloc API로 할당된 heap영역에 WriteProcessMemory API로 복호화한 PE 데이터를 넣고 이 영역의 주소를 rundll32.exe의 image base로 변경한다.
rundll32.exe의 새로운 EP는 SetThreadContext API의 매개변수인 [ebp-2EC]에서 0xCC만큼 떨어진 [EBP-220] (=image base+1F1F) 이다. SetThreadContext로 PE의 EP가 저장된 스레드 컨텍스트를 변경한다.
변경 후 ResumeThread를 실행하면 suspend였던 rundll32.exe 프로세스가 시작된다.
이때 이 프로세스는 디버거에서만 작동되기 때문에 x64dbg에서 attach시킨 후 진행해야 한다.
기존에 malwarebytes의 가이드에 따르면 [EBP-220]에 저장된 새로운 EP위치를 찾아가 BP를 건 후 계속 진행해야 하지만 무슨 이유에서인지 그렇게 하면 성공 문자열이 출력되지 않는다.
(rundll32.exe도 간단하게 분석해보았지만 이유를 찾을 수 없었다 ㅠ)
따라서 진입점 중단점에 BP를 걸고 진행한다.
Rundll32.exe는 프로세스 익스플로러에 쉘코드를 인젝션시켜 메시지를 출력하는 쉘코드가 삽입되어 있다.
성공메시지가 출력되기 위해서는 현재 디버깅 중인 rundll32.exe가 system32경로이고, 실행중인 프로세스 윈도우 이름 중 프로세스 익스플로러(procexpl.exe)가 있어야 한다.
suspend상태였던 프로세스를 진행시키면 jmp~코드로 진행된다.
F9로 아까 설정했던 BP까지 진행한 후 이어서 진행하다 보면 쉘코드가 실행되어 process explorer 프로그램이 인젝션되어 프로그램 대신 성공 문자열이 뜨게 된다.
조건이 맞지 않는 경우 다음과 같이 stage2, 실패 문자열이 포함된 메시지박스가 나온다.
'Security > Reversing' 카테고리의 다른 글
명령어 집합구조 (ISA) (0) | 2022.07.20 |
---|---|
데이터 보호 기법-Canary (0) | 2021.11.11 |
UAF 공격 (0) | 2021.11.06 |
[CodeEngn] Challenges Basic RCE L20 (0) | 2021.10.18 |
디버깅 탐지 방법 (0) | 2021.10.15 |
댓글