윈도우즈 말웨어 분석 방법론 - FormBook 디텍션

FormBook 말웨어는 지난 몇년간 점점 더 세력을 확장하고 있는 말웨어 패밀리입니다. 최근 S2W랩에서 FormBook Tracker unveiled on the dark web이라는 리포트 발표를 통하여 FormBook의 위험성을 알리고, 주의를 다시 한번 환기 시키는 기회가 되었습니다. 이번 아티클을 통해서 FormBook에 대한 개괄적인 설명과 함께 자신의 시스템에 FormBook 감염이 되었는지를 쉽게 알아 낼 수 있는 방법들을 알아 보겠습니다.

감염 경로

FormBook은 주로 스팸 피슁 메일을 통해서 감염되고, CVE-2017-8570 RTF 취약점을 이용하여 전파되는 것으로 알려져 있습니다. 또한 goriot/lazarus와 같은 .Net 말웨어 패밀리들에 딸려 감염되는 경향성도 최근에 파악 되고 있습니다. 또한 최근 웹기반 샌드박스 시스템인 any.run에 의하면 FormBook은 서브미션 되는 샘플 10위권에 들어 갈 정도로 많이 전파되는 말웨어의 하나입니다.

감염 의도

FormBook은 정보 탈취를 목적으로 하는 Stealer에 해당하는 말웨어입니다. 또한 금융과 관련된 여러 정보들에 대한 수집에 집중하는 것으로 알려져 있습니다.

Anti-Analysis, Anti-Debugging

FormBook은 특히 분석을 방해하기 위한 여러 장치들과 함께 디버깅을 어렵게 만드는 여러 트릭들을 사용하고 있습니다.

프로세스 블랙 리스팅

다음과 같은 프로세스들에 대해서 체크하고, 해당 프로세스들이 발견될 때에 활동을 중지하는 것으로 알려져 있습니다.

  • VMWare:
Vmtoolsd.exe, vmwareservice.exe, vmwareuser.exe
  • Virtual Box:
vboxservice.exe, vboxtray.exe
  • Analysis tools:
netmon.exe, Sandboxiedcomlaunch.exe, Sandboxierpcss.exe, procmon.exe, filemon.exe, wireshark.exe, prl_tools_service.exe, vmsrvc.exe, Vmusrvc.exe, python.exe, perl.exe, regmon.exe

디버거 체크

FormBook은 또한 NtQuerySystemInformation 콜등을 사용하여 말웨어가 디버거에 어태치 되어 있는지를 체크하는 것으로 알려져 있습니다. 또한 유저 레벨의 디버거 뿐만 아니라 커널 레벨 디버거까지 체크합니다.

난독화된 스트링

많은 말웨어들이 메모리에서는 스트링을 모두 난독화 해제 하여 로딩하고 사용하는 경향성이 있지만, 이 말웨어는 이러한 스트링이 메모리상에서도 난독화된 상태로 남아 있어서, 메모리 덤프를 통한 스트링 확인 등의 방법론을 사용하기 힘들게 만듭니다. 이러한 경향성은 최근 말웨어들 사이에서 점점 많아지는 현상입니다.

String Hashing

특정 스트링에 대한 체크를 할 때에 strcmp 등의 함수를 사용하면서 해당 스트링이 메모리에 흔적을 남기는 것을 최소화하도록 스트링의 CRC32 해쉬 값을 사용하여 특정 스트링 검색과 비교를 합니다. 예를 들어 어떠한 API를 Export table 등에서 검색할 때에 스트링 단위의 검색이 아닌 CRC32 해쉬값을 통한 검색을 사용합니다. 이러한 방법은 기존 쉘코드등에서 쉘코드 크기를 줄이고 쉘코드를 쉽게 리버싱 하지 못하게 하려는 의도로 자주 사용되는 기법입니다.

API 후킹 메커니즘

이 말웨어의 의도는 정보 수집으로서 Firefox, Opera, Chrome 등의 웹브라우저와 notepad.exe, explorer.exe등 여러 프로세스에 자신의 코드를 인젝션하고 다수의 API들을 후킹하는 특징을 가지고 있습니다. 이러한 후킹을 통해서 사용자의 웹 트래픽이나 Keystroke, Clipboard 등의 데이타를 뽑아 내는 것이 목적으로 보입니다.

이 말웨어는 call/pop/retn 인스트럭션 셋을 사용하는 특이한 후킹 코드를 가지고 있으며, IDA나 디버거를 통한 쉬운 컨트롤 플로우 분석을 방해하기 위한 목적과 함께 특정 후킹 코드가 해당 오리지널 함수를 부른후에 다시 부르게 만들 수 있는 트릭으로서의 효과도 가지고 있는 것으로 보입니다.

이러한 과정은 다음과 같은 다이어그램으로 표현될 수 있습니다.

예를 들어 Original Function이 USER32!GetMessageA라고 하면, 해당 함수의 앞부분에 다음과 같은 후킹 코드를 설치합니다.

0:000> u user32!GetMessageA
user32!GetMessageA:
00007fff`b194e8b0 48b813086e705f020000 mov rax,25F706E0813h
00007fff`b194e8ba ffd0            call    rax

예를 들어 다음 GetMessageStackSetter는 USER32!GetMessageHook* 함수가 후킹 되면서 불리는 코드로서 위의 다이어그램에서는 StackSetter 에 해당합니다 GetMessageStackSetter는 GetMessagePostHookCalculator (Post-Hook Address Calculator)를 호출하게 됩니다.

0000025F706DA056                               GetMessageStackSetter proc near               ; CODE XREF: GetMessageHook1↓j
...
0000025F706DA06A 50                                            push    rax
0000025F706DA06B 50                                            push    rax
0000025F706DA06C E8 0B 00 00 00                                call    GetMessagePostHookCalculator

GetMessagePostHookCalculator의 코드는 다음과 같이 “call $+5/pop rax/retn” 세개의 인스트럭션들로 구성되어 있습니다. 이 세 인스트럭션을 통해서 결과적으로 “rax”에 GetMessagePostHookCalculator+5의 주소(0000025F706DA081)를 할당할 수 있습니다.

0000025F706DA07C                               GetMessagePostHookCalculator proc near               ; CODE XREF: GetMessageStackSetter+16↑p
0000025F706DA07C E8 00 00 00 00                                call    $+5
0000025F706DA081 58                                            pop     rax
0000025F706DA082 C3                                            retn
0000025F706DA082                               GetMessagePostHookCalculator endp

GetMessagePostHookCalculator에서 다시 GetMessageStackSetter로 리턴된 후에도 여전히 rax에는 해당 주소값이 존재합니다. GetMessagePostHookCalculator 콜에서 리턴한 주소인 0000025F706DA071에서 해당 rax에 2를 추가해주고 해당 값을 스택 주소인 [rsp+8]에 씁니다. 이 주소는 바로 직전에 GetMessage*를 부른 이후에 리턴할 주소입니다.

0000025F706DA056                               GetMessageStackSetter proc near               ; CODE XREF: GetMessageHook1↓j
...
0000025F706DA06A 50                                            push    rax
0000025F706DA06B 50                                            push    rax
0000025F706DA06C E8 0B 00 00 00                                call    GetMessagePostHookCalculator
0000025F706DA071 48 83 C0 02                                   add     rax, 2            <---
0000025F706DA075 48 89 44 24 08                                mov     [rsp+8], rax
0000025F706DA07A C3                                            retn

즉 이 후킹 코드가 GetMessage* 함수로 리턴한 후에 다시 GetMessage* 함수가 리턴할 때에 GetMessagePostHookCalculator 함수 다음 주소에 위치한 함수인 GetMessagePostHook이 실행되게 됩니다.

0000025F706DA083                               GetMessagePostHook proc near
0000025F706DA083
0000025F706DA083                               var_4           = dword ptr -4
0000025F706DA083                               arg_18          = qword ptr  20h
0000025F706DA083                               arg_20          = qword ptr  28h
0000025F706DA083
0000025F706DA083 68 B3 F5 6D 70                                push    706DF5B3h
0000025F706DA088 C7 44 24 04 5F 02 00 00                       mov     [rsp+8+var_4], 25Fh
...

스택을 통한 스트링 조합

이 말웨어는 또하나의 특이한 행위를 보여 줍니다. 최대한 스트링이 재조합되는 과정을 숨기기 위해서 어떠한 스트링을 사용하기 위해서 스트링들을 스택에 푸쉬하는 방식으로 재조합하는 행동을 보여 줍니다. 예를 들어 다음과 같은 형태의 코드들이 자주 발견됩니다.

0000025F706D0621 C7 45 B0 75 00 73 00                          mov     dword ptr [rbp-50h], 730075h
0000025F706D0628 48 89 44 24 20                                mov     [rsp+20h], rax
0000025F706D062D C7 45 B4 65 00 72 00                          mov     dword ptr [rbp-4Ch], 720065h
0000025F706D0634 C7 45 BA 32 00 2E 00                          mov     dword ptr [rbp-46h], 2E0032h
0000025F706D063B C7 45 BE 64 00 6C 00                          mov     dword ptr [rbp-42h], 6C0064h
0000025F706D0642 C7 45 C2 6C 00 00 00                          mov     dword ptr [rbp-3Eh], 6Ch ; 'l'
0000025F706D0649 C7 45 C8 73 00 73 00                          mov     dword ptr [rbp-38h], 730073h
0000025F706D0650 C7 45 CC 70 00 69 00                          mov     dword ptr [rbp-34h], 690070h
0000025F706D0657 C7 45 D0 63 00 6C 00                          mov     dword ptr [rbp-30h], 6C0063h
0000025F706D065E C7 45 D4 69 00 2E 00                          mov     dword ptr [rbp-2Ch], 2E0069h
0000025F706D0665 C7 45 D8 64 00 6C 00                          mov     dword ptr [rbp-28h], 6C0064h
0000025F706D066C C7 45 DC 6C 00 00 00                          mov     dword ptr [rbp-24h], 6Ch ; 'l'
0000025F706D0673 C7 45 E0 77 00 69 00                          mov     dword ptr [rbp-20h], 690077h
0000025F706D067A C7 45 E4 6E 00 69 00                          mov     dword ptr [rbp-1Ch], 69006Eh
0000025F706D0681 C7 45 E8 6E 00 65 00                          mov     dword ptr [rbp-18h], 65006Eh
0000025F706D0688 C7 45 EC 74 00 2E 00                          mov     dword ptr [rbp-14h], 2E0074h
0000025F706D068F C7 45 F0 64 00 6C 00                          mov     dword ptr [rbp-10h], 6C0064h
0000025F706D0696 C7 45 F4 6C 00 00 00                          mov     dword ptr [rbp-0Ch], 6Ch ; 'l'

이 코드들의 “mov dword ptr[rbp-*]” 형태의 인스트럭션들을 통해서 다음과 같은 바이트들이 스택으로 푸쉬됩니다.

75 00 73 00
65 00 72 00
32 00 2E 00
64 00 6C 00
6C 00 00 00
73 00 73 00
70 00 69 00
63 00 6C 00
69 00 2E 00
64 00 6C 00
6C 00 00 00
77 00 69 00
6E 00 69 00
6E 00 65 00
74 00 2E 00
64 00 6C 00
6C 00 00 00

간단히 헥스 에디터를 통해서 해당 스트링을 아스키 형태로 변환해서 보면 다음과 같이 user32.dll, sspicli.dll, wininet.dll과 같은 DLL 이름들로 변환됨을 알 수 있습니다.

Offset(h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F

00000000  75 00 73 00 65 00 72 00 32 00 2E 00 64 00 6C 00  u.s.e.r.2...d.l.
00000010  6C 00 00 00 73 00 73 00 70 00 69 00 63 00 6C 00  l...s.s.p.i.c.l.
00000020  69 00 2E 00 64 00 6C 00 6C 00 00 00 77 00 69 00  i...d.l.l...w.i.
00000030  6E 00 69 00 6E 00 65 00 74 00 2E 00 64 00 6C 00  n.i.n.e.t...d.l.
00000040  6C 00 00 00                                      l...

디텍션 룰의 작성

기업 환경에서 말웨어를 헌팅하기 위해서 자주 사용되는 툴 중의 하나가 바로 Yara입니다. Yara는 VirusTotal의 한 프로젝트로 시작하여 이제는 인더스트리의 표준적인 툴로 사용되고 있습니다. FormBook의 경우 바이너리 파일의 경우 여러 변형이 존재하기 때문에 주로 메모리를 스캐닝하는 방식으로 쉽게 디텍션 가능합니다.

2020년 7월 현재 돌아 다니는 변종들의 경우에는 CAPE Sandbox의 결과에 의하면, shellcode_get_eip, shellcode_stack_strings, Formbook 등의 Yara 룰들에 의한 디텍션이 가능함을 알 수 있습니다. 이러한 Yara 룰들의 경우 해당 인펙션 바이너리에 대해서 미리 알고 있어야 하는 문제가 있습니다. 어떠한 바이너리가 FormBook 바이너리인지 인지하기 힘든 상황에서는 FormBook Yara 룰이 잘 작동하지 않습니다.

다른그림에서는 여러 테스트를 통해서 FormBook의 인젝션 모듈을 디텍션할 수 있는 Yara 룰셋을 특정하였습니다.

rule Formbook {

    meta:
        author = "Matt Oh @ DarunGrim"
        date = "2020-07-22"
        version = "1"
        description = "Simple Rule For Catching Usual Formbook Hook Signatures"

    strings:
        $call_pop_retn = { E8 00 00 00 00 58 C3 }
            // seg000:0000025F706DA0F2 E8 00 00 00 00                 call    $+5
            // seg000:0000025F706DA0F7 58                             pop     rax
            // seg000:0000025F706DA0F8 C3                             retn

        // dword stack string near the frame pointer.
        // the compiler may choose to use a single byte offset from $bp.
        // it may move four bytes at a time onto the stack.
        // like: mov [ebp-10h], 680073h  ; "sh"
        //
        // regex explanation:
        //   2 times:
        //     byte C7          (mov dword)
        //     byte 45          ($bp-relative, one-byte offset)
        //     any byte         (the offset from $bp)
        //     printable ascii  (the immediate constant)
        //     byte 00          (second byte of utf-16 encoding of ascii character)
        //     printable ascii  (the immediate constant)
        //     byte 00          (second byte of utf-16 encoding of ascii character)
        //   1 times:
        //     byte C7          (mov dword)
        //     byte 45          ($bp-relative, one-byte offset)
        //     any byte         (the offset from $bp)
        //     any byte         (immediate constant or NULL terminator)
        //     byte 00          (the immediate constant, NULL terminator)
        //     byte 00          (the immediate constant, NULL terminator)
        //     byte 00          (the immediate constant, NULL terminator)
        $ss_small_bp_dword = /(\xC7\x45.[a-zA-Z0-9 -~]\x00[a-zA-Z0-9 -~]\x00){2,}\xC7\x45..\x00\x00\x00/

    condition:
        all of them
}

위의 call_pop_retn룰은 shellcode_get_eip의 변형으로서 call-pop-retn 명령어 셋으로 조금더 특정하여 FP 확률을 낮췄습니다. 두번째 ss_small_bp_dword 패턴의 앞의 스택을 사용한 스트링 재조합을 탐지 하는 패턴으로서 CAPE/data/yara/memory/shellcodes.yar의 shellcode_stack_strings룰의 한 패턴을 차용하였습니다.

사용법

  1. 다른그림의 MalwareHuntingRules에서 위의 formbook.yar 룰을 다운로드 받습니다.
  2. Windows용 Yara Releases를 다운로드 받습니다.
  3. 위의 두 파일을 테스트 대상 시스템에 복사합니다.
  4. FormBook은 디폴트로 explorer.exe를 감염시키므로, 해당 프로세스의 pid를 얻어 냅니다. pslist를 사용할 경우 다음과 같은 명령을 사용할 수 있습니다.
pslist explorer.exe

Process Explorer를 사용할 경우 다음과 같이 explorer.exe의 프로세스 ID를 얻어 냅니다.

  1. 다음과 같이 yara를 실행 시킵니다. 예를 들어 process id가 2844일 경우 formbook.yar의 룰들을 사용하여 해당 프로세스에 대한 스캐닝을 수행합니다. 만약 Formbook 패턴이 발견될 경우 아래와 같이 “Formbook”이라는 스트링이 프린트 됩니다.
C:\>yara64.exe formbook.yar 2844
Formbook 2844
  1. 만약 FormBook 패턴이 발견되었을 경우 더 자세한 메시지를 보기 위해서 다음과 같이 -s -g 옵션을 주어 주소와 발견된 패턴을 덤프해 볼 수 있습니다. 이렇게 디텍션된 메모리들은 프로세스를 덤프하여 메모리 내용을 extraction하여 추가적인 분석이 가능합니다.
C:\>yara64.exe -s -g formbook.yar 2844
Formbook [] 2844
0xe1e407c:$call_pop_retn: E8 00 00 00 00 58 C3
0xe1e40f2:$call_pop_retn: E8 00 00 00 00 58 C3
0xe1e416e:$call_pop_retn: E8 00 00 00 00 58 C3
0xe1e4175:$call_pop_retn: E8 00 00 00 00 58 C3
...
0xe1da62d:$ss_small_bp_dword: \xC7E\xB4e\x00r\x00\xC7E\xBA2\x00.\x00\xC7E\xBEd\x00l\x00\xC7E\xC2l\x00\x00\x00
0xe1da634:$ss_small_bp_dword: \xC7E\xBA2\x00.\x00\xC7E\xBEd\x00l\x00\xC7E\xC2l\x00\x00\x00
0xe1da649:$ss_small_bp_dword: \xC7E\xC8s\x00s\x00\xC7E\xCCp\x00i\x00\xC7E\xD0c\x00l\x00\xC7E\xD4i\x00.\x00\xC7E\xD8d\x00l\x00\xC7E\xDCl\x00\x00\x00
0xe1da650:$ss_small_bp_dword: \xC7E\xCCp\x00i\x00\xC7E\xD0c\x00l\x00\xC7E\xD4i\x00.\x00\xC7E\xD8d\x00l\x00\xC7E\xDCl\x00\x00\x00
0xe1da657:$ss_small_bp_dword: \xC7E\xD0c\x00l\x00\xC7E\xD4i\x00.\x00\xC7E\xD8d\x00l\x00\xC7E\xDCl\x00\x00\x00
0xe1da65e:$ss_small_bp_dword: \xC7E\xD4i\x00.\x00\xC7E\xD8d\x00l\x00\xC7E\xDCl\x00\x00\x00
...

결론

FormBook은 분석이 꽤 까다로운 말웨어에 속합니다. 분석이 까다로울 수록 안티 말웨어 입장에서도 디텍션에 문제가 생길 확률이 높습니다. 특히 시스템의 분석 환경에 민감하고, 샌드박스 우회 등을 시도하는 경향성이 있어서 많은 분석 시스템에서 제대로 디텍션되지 않을 확률도 높아집니다. 또한 이러한 문제는 결국 안티 말웨어 제품의 시그너쳐 품질 저하로 이어질 수 있습니다. 이러한 경우 EDR 등의 행동 기반 탐지가 도움이 될 수 있습니다. 이 말웨어의 경우 Process Hollowing 이나 유저랜드 APC 인젝션 등의 기법을 사용하고 있어서 그러한 기법을 디텍션 할 수 있는 센서를 내장한 경우 쉽게 디텍션 될 수 있습니다.

트레이닝

다른그림은 Threat 인텔리젼스와 지식 제공 회사로서 다음과 같은 Windows Malware 분석과 관련된 트레이닝을 제공합니다. 많은 관심 바랍니다. 이 트레이닝을 통해서 위에 소개된 기법들과 유사한 말웨어들의 분석 회피 기법들을 더 자세하게 배우실 수 있습니다.