Daniel Stepanic

GULOADER로 끈적끈적해지기: 다운로더 난독화 해제하기

Elastic Security Labs에서 업데이트된 GULOADER 분석 대책을 안내합니다.

13분 읽기Malware 분석
GULOADER로 끈적끈적해지기: 다운로더 난독화 해제하기

Overview

엘라스틱 보안 연구소는 수년 동안 지속적으로 개발되면서 활발히 활동 중인 회피형 셸코드 다운로더인 CloudEyE라고도 알려진 GULOADER와 같은 활성 위협을 지속적으로 모니터링하고 있습니다. 이러한 최근 변경 사항 중 하나는 새로운 캠페인에서 벡터화된 예외 처리기(VEH)에 예외를 추가하여 이미 긴 분석 방지 트릭 목록에 더 많은 복잡성을 추가한 것입니다.

지난 몇 년 동안 GULOADER의 핵심 기능은 크게 변하지 않았지만, 난독화 기술의 지속적인 업데이트로 인해 GULOADER를 분석하는 데 많은 시간과 리소스가 소요됩니다. 이 게시물에서는 GULOADER를 트리거할 때 다음과 같은 주제를 다룹니다:

  • 초기 셸코드 및 언패킹 프로세스 검토하기
  • 복호화된 셸 코드의 진입점 찾기
  • 제어 흐름을 난독화하는 GULOADER의 VEH 업데이트 논의
  • VEH를 패치하는 방법론 제공

초기 셸코드

샘플에서 GULOADER는 NSIS(Nullsoft 스크립트 가능 설치 시스템) 설치 프로그램에 미리 패키징되어 제공됩니다. 인스톨러가 추출되면 주요 구성 요소는 다음과 같습니다:

  • NSIS 스크립트 - 이 스크립트 파일은 다양한 구성 및 설치 측면을 모두 설명합니다.

  • System.dll - $PLUGINSDir. 이 파일은 임시 폴더에 드롭되어 GULOADER 셸코드를 할당/실행합니다.

  • 셸코드 - 암호화된 셸코드가 중첩된 폴더에 묻혀 있습니다.

셸코드를 호스팅하는 파일을 찾아내는 한 가지 빠른 방법은 GULOADER를 실행한 후 SysInternal의 프로세스 모니터에서 ReadFile 이벤트를 모니터링하는 것입니다. 이 경우 셸코드가 파일(Fibroms.Hag)에서 읽혀진 것을 볼 수 있습니다.

GULOADER는 다양한 Windows API 함수를 사용하여 콜백을 통해 셸코드를 실행합니다. 그 주된 이유는 CreateRemoteThread 또는 WriteProcessMemory 과 같이 프로세스 주입에 사용되는 기존 Windows API를 중심으로 탐지되는 것을 피하기 위해서입니다. 저희는 EnumResourceTypesACallWindowProcW 을 관찰했습니다.

에 대한 MSDN 문서를 검토하면 EnumResourceTypesA에 대한 MSDN 문서를 살펴보면 두 번째 매개변수가 콜백 함수에 대한 포인터를 기대한다는 것을 알 수 있습니다. 위의 스크린샷에서 새로 할당된 셸코드가 이 인수에 배치된 것을 볼 수 있습니다.

주요 셸코드 진입점 찾기

최근 샘플에서 GULOADER는 초기 셸코드 시작 시 다양한 정크 명령어와 점프를 포함시켜 복잡성을 증가시켰습니다. 다운로더를 리버스 엔지니어링하려면 일부 툴링에서 분해 및 제어 흐름을 방해하도록 설계된 코드 난독화를 해제하는 긴 프로세스를 처리해야 하므로 핵심 GULOADER 셸 코드의 실제 시작 부분을 찾는 데 어려움을 겪을 수 있습니다.

초기 호출을 찾는 한 가지 방법론은 x64dbg 내부의 그래프 보기를 활용하고 아래에서 위로 접근하여 call eax 인스트럭션을 찾는 것입니다.

초기 제어 흐름을 추적하는 또 다른 기법으로는 리버스 엔지니어링 프레임워크인 Miasm을활용하는 방법이 있습니다. 다음은 셸코드를 전달하고 지침을 분해하여 흐름을 따라갈 수 있는 간단한 예제입니다:

from miasm.core.locationdb import LocationDB
from miasm.analysis.binary import Container
from miasm.analysis.machine import Machine

with open("proctoring_06BF0000.bin", "rb") as f:
    code = f.read()

loc_db = LocationDB()
c = Container.from_string(code, loc_db)

machine = Machine('x86_32')
mdis = machine.dis_engine(c.bin_stream, loc_db=loc_db)
mdis.follow_call = True 
mdis.dontdis_retcall = True
asm_cfg = mdis.dis_multiblock(offset=0x1400)

Miasm은 142 jmp 지침을 잘라내어 EAX(주소: 0x3bde)로의 호출 지침에서 중지하도록 구성한 정크 지침을 탐색합니다.

JMP        loc_3afd
->	c_to:loc_3afd 
loc_3afd
MOV        EBX, EAX
FADDP      ST(3), ST
PANDN      XMM7, XMM2
JMP        loc_3b3e
->	c_to:loc_3b3e 
loc_3b3e
SHL        CL, 0x0
PSRAW      MM1, MM0
PSRLD      XMM1, 0xF1
JMP        loc_3b97
->	c_to:loc_3b97 
loc_3b97
CMP        DL, 0x3A
PADDW      XMM3, XMM5
PXOR       MM3, MM3
JMP        loc_3bde
->	c_to:loc_3bde 
loc_3bde
CALL       EAX

미아즘의 꼬리 끝

GULOADER’s VEH Update

GULOADER의 특징적인 기술 중 하나는 벡터화된 예외 처리 (VEH) 기능에 집중되어 있습니다. 이 기능을 사용하면 Windows 애플리케이션에서 예외가 표준 예외 프로세스를 통해 라우팅되기 전에 예외를 가로채서 처리할 수 있습니다. 멀웨어 제품군과 소프트웨어 보호 애플리케이션은 이 기술을 사용하여 분석가와 툴링이 악성 코드를 추적하기 어렵게 만듭니다.

GULOADER는 RtlAddVectoredExceptionHandler 를 사용하여 VEH를 추가하여 이 프로세스를 시작합니다. GULOADER 셸코드를 실행하는 동안 이러한 다양한 예외를 트리거하도록 의도적으로 배치된 코드가 있습니다. 이러한 예외가 트리거되면 VEH는 하드웨어 중단점을 확인합니다. 찾을 수 없는 경우, GULOADER는 예외가 발생한 곳에서 1바이트 오프셋이 있는 1바이트 XOR 키(샘플당 변경)를 사용하여 CONTEXT 구조를 통해 직접 EIP를 수정합니다. 다음 섹션에서 이 기법의 구체적인 예를 살펴보겠습니다. 아래는 샘플의 VEH를 디컴파일한 것입니다:

이 기술은 새로운 기술은 아니지만, GULOADER는 시간이 지남에 따라 새로운 예외를 계속 추가하고 있으며, 최근 몇 달 동안 이 두 가지 예외가 추가된 것을 확인했습니다:

  • EXCEPTION_PRIV_INSTRUCTION
  • EXCEPTION_ILLEGAL_INSTRUCTION

GULOADER에 새로운 예외가 추가되면 연구자의 분석 프로세스를 신속하게 처리하는 데 사용되는 툴링이 중단될 수 있습니다.

EXCEPTION_PRIV_INSTRUCTION

최근에 추가된 두 가지 예외를 통해 VEH 워크플로우를 따라가 보겠습니다. 첫 번째 예외(EXCEPTION_PRIV_INSTRUCTION)는 허용되지 않는 권한 수준에서 프로세서의 명령어 집합에서 권한 있는 명령어를 실행하려고 시도할 때 발생합니다. 아래 WRSMR 예시와 같은 특정 명령어는 커널 레벨의 권한을 요구하므로 사용자 모드에서 프로그램을 실행하면 잘못된 권한으로 인해 예외가 트리거됩니다.

EXCEPTION_ILLEGAL_INSTRUCTION

이 예외는 프로그램이 유효하지 않거나 정의되지 않은 CPU 명령을 실행하려고 시도할 때 호출됩니다. 샘플에서는 vmclear 또는 vmxon 과 같은 인텔 가상화 명령어를 만나면 예외가 트리거됩니다.

예외가 발생하면 GULOADER VEH 코드가 먼저 어떤 예외 코드가 예외를 발생시켰는지 확인합니다. 샘플에서는 예외가 아래 5가지 중 하나라도 일치하면 코드가 동일한 경로를 따르게 됩니다.

  • EXCEPTION_ACCESS_VIOLATION
  • EXCEPTION_ILLEGAL_INSTRUCTION
  • EXCEPTION_PRIV_INSTRUCTION
  • EXCEPTION_SINGLE_STEP
  • EXCEPTION_BREAKPOINT

그런 다음 GULOADER는 EXCEPTION_POINTERS 구조체 내부에 있는 CONTEXT 레코드를 탐색하여 하드웨어 중단점이 있는지 확인합니다. 다른 디버그 레지스터에서 하드웨어 중단점이 발견되면 GULOADER는 CONTEXT 레코드에 0 를 반환하고, 결국 셸코드가 충돌하게 됩니다.

하드웨어 중단점이 없는 경우, GULOADER는 예외를 발생시킨 주소에서 7 바이트 떨어진 단일 바이트를 검색합니다. 마지막 예제를 vmclear 와 함께 사용하면 바이트(0x8A)를 검색합니다.

그런 다음 해당 바이트를 사용하여 다른 하드코딩된 바이트로 XOR 연산을 수행합니다. 저희의 경우(0xB8)는 샘플마다 고유합니다. 이제 파생된 오프셋 0x32 (0xB8 ^ 0x8A)을 사용하여 GULOADER는 예외를 발생시킨 이전 주소 (0x7697630)에 0x32 를 추가하여 CONTEXT 레코드에서 직접 EIP 주소를 수정하여 다음 코드가 주소 (0x7697662)에서 실행되도록 합니다.

중간에 다른 정크 인스트럭션이 있고 예외가 반복적으로 발생하면(샘플에서 229 고유 예외를 계산했습니다), 이것이 왜 다른 툴을 망가뜨리고 분석가의 시간을 증가시키는지 어렵지 않게 알 수 있습니다.

제어 흐름 청소

제어 흐름을 더 쉽게 추적하기 위해 분석가는 실행을 추적하고, 예외를 기록하고, 앞서 설명한 EIP 수정 알고리즘을 사용하여 셸코드를 패치함으로써 VEH를 우회할 수 있습니다. 이 절차에서는 동적 바이너리 계측 프레임워크인 Pin을 활용하는 @hasherezade가 개발한 도구인 TinyTracer를 활용했습니다. 이렇게 하면 예외를 트리거한 다른 주소를 파악할 수 있으므로 위의 예제에서 vmclear 를 사용하면 주소가 0x7697630 이며 사용자 모드 예외를 처리하는 함수인 KiUserExceptionDispatcher 를 호출하는 예외를 생성한 것을 확인할 수 있습니다.

모든 예외가 수집되고 필터링되면, 이를 IDAPython 스크립트로 전달하여 각 주소를 살펴보고 7번째 바이트 오버와 XOR 키(0xB8)를 사용하여 오프셋을 계산한 다음 짧은 점프로 예외를 생성하는 모든 명령어를 패치할 수 있습니다.

다음 이미지는 0x076976300x0769766C 주소에서 예외를 트리거하는 패치 지침의 예입니다.

아래는 패치가 전역적으로 적용되기 전의 제어 흐름 그래프를 나타내는 그래픽입니다. vmclear 인스트럭션이 있는 기본 블록은 주황색으로 강조 표시됩니다. VEH를 구현함으로써 GULOADER는 제어 흐름 그래프를 평평하게 만들어 프로그램 로직을 추적하기 더 어렵게 만듭니다.

jmp 명령어로 VEH를 패치한 후 기본 블록을 서로 연결하여 셸코드 흐름의 복잡성을 줄임으로써 기본 블록을 변환합니다.

이 기술을 사용하면 청소 프로세스를 가속화할 수 있지만, 이 방법이 완벽한 방법은 아니라는 점에 유의하세요. 이 경우에도 여전히 분석해야 할 코드/기능이 많이 남아 있지만, VEH를 제거하면 코드를 간소화하는 데 큰 도움이 됩니다. 전체 POC 스크립트는 여기에서 확인할 수 있습니다.

결론

GULOADER에는 분해를 방해하고 제어 흐름을 방해하며 연구자의 분석을 어렵게 만들 수 있는 다양한 기능이 있습니다. 프로세스가 불완전하지만, 다양한 정적 또는 동적 프로세스를 통해 이러한 특성에 대응하여 분석 시간을 단축할 수 있습니다. 예를 들어, VEH의 새로운 예외를 사용하면 여전히 이를 추적하여 셸코드를 패치할 수 있다는 것을 확인했습니다. 이 프로세스를 통해 분석가는 GULOADER의 핵심 기능에 더 가까이 접근할 수 있는 올바른 경로를 찾을 수 있습니다.

저희의 워크플로우 일부를 공유함으로써, 실제 현장에서 GULOADER를 마주했을 때 여러 가지 시사점을 얻을 수 있기를 바랍니다. 구로더의 변화에 따라 향후에는 새롭고 다양한 전략이 필요할 가능성이 높습니다. GULOADER를 탐지하기 위해 다음 섹션에는 YARA 규칙이 포함되어 있으며, 이 게시물의 IDAPython 스크립트는 여기에서 확인할 수 있습니다. 최신 위협 연구에 대한 새로운 업데이트는 Elastic Security Lab 팀의 맬웨어 분석 섹션에서 확인하세요.

YARA

Elastic Security는 이 활동을 식별하기 위해 다양한 YARA 규칙을 만들었습니다. 아래는 GULOADER를 식별하기 위한 YARA 규칙의 한 예입니다.

rule Windows_Trojan_Guloader {
    meta:
        author = "Elastic Security"
        creation_date = "2023-10-30"
        last_modified = "2023-11-02"   
        reference_sample = "6ae7089aa6beaa09b1c3aa3ecf28a884d8ca84f780aab39902223721493b1f99"
        severity = 100
        arch = "x86"
        threat_name = "Windows.Trojan.Guloader"
        license = "Elastic License v2"
        os = "windows"
    strings:
        $djb2_str_compare = { 83 C0 08 83 3C 04 00 0F 84 [4] 39 14 04 75 }
        $check_exception = { 8B 45 ?? 8B 00 38 EC 8B 58 ?? 84 FD 81 38 05 00 00 C0 }
        $parse_mem = { 18 00 10 00 00 83 C0 18 50 83 E8 04 81 00 00 10 00 00 50 }
        $hw_bp = { 39 48 0C 0F 85 [4] 39 48 10 0F 85 [4] 39 48 14 0F 85 [7] 39 48 18 }
        $scan_protection = { 39 ?? 14 8B [5] 0F 84 }
    condition:
        2 of them
}

관찰

모든 관측값은 ECS 및 STIX 형식으로도 다운로드할 수 있습니다.

이 연구에서는 다음과 같은 관찰 가능성에 대해 논의했습니다.

Observable유형이름참조
6ae7089aa6beaa09b1c3aa3ecf28a884d8ca84f780aab39902223721493b1f99SHA-256Windows.Trojan.GuloaderGULOADER 다운로더
101.99.75[.]183/MfoGYZkxZIl205.binURL.NAGULOADER C2 URL
101.99.75[.]183IPv4-addrNAGULOADER C2 IP

참고 자료