Salim Bitam

PHANTOMPULSE: 해킹 가능한 블록체인 기반 C2 RAT의 구조

Elastic Security Labs는 REF6598 침입 세트를 통해 암호화폐 업계 피해자들에게 유포된 장기 활동형 RAT인 PHANTOMPULSE에 대한 상세한 리버스 엔지니어링 분석 결과를 발표합니다.

Elastic Security Labs가 이전에 다룬 REF6598 사례에서는, Obsidian 플러그인 악용을 통해 Windows 툴체인이 유입되고, 메모리 내 PE 로더(PHANTOMPULL)를 통해 권한을 상승시킨 뒤, RAT(PHANTOMPULSE)로 마무리되는 일련의 침입 과정이 기록되었습니다. 그 게시물은 배송에 중점을 두었습니다. 이 글에서는 마지막 단계인 ‘PHANTOMPULSE’를 분석합니다. PHANTOMPULSE는 세 가지 프로세스 주입 기법을 포함하고 있으며, 이더리움/베이스/옵티미즘 트랜잭션 입력을 통해 C2를 해결하고, 공개된 ‘ schuac ’ 기법을 통해 UAC를 우회합니다. 이번 분석을 통해 싱크홀(sinkhole)이 가능한 블록체인 C2 채널과, AMSI/WLDP/ETW를 비활성화하는 통합 하드웨어 중단점(hardware-breakpoint) 기본 요소, 그리고 임플란트의 디버그 문자열에 포함된 광범위한 AI 지원 개발 흔적이 드러났다.

핵심 사항

  • PHANTOMPULSE는 최근 공개된 공격적 보안 PoC에서 차용한 세 가지 주입 기법을 구현합니다.
  • AMSI, WLDP 및 ETW는 단일 공유 HWBP 프리미티브를 통해 우회됩니다
  • 블록체인 C2 리졸버에는 발신자 인증 기능이 없어, 공격자가 단 한 건의 트랜잭션을 전송하는 것만으로 모든 임플란트의 C2 URL을 재정의할 수 있습니다
  • 바이너리 파일에 강력한 AI 지원 개발 지표가 포함되어 있음

AI 지원 개발에 관한 참고 사항

PHANTOMPULSE는 디버그 문자열 전반에 걸쳐 AI 코딩 지원의 흔적이 뚜렷이 드러납니다.

가장 확실한 신호:

  • 운영 로그의 체계적인 단계 번호 지정: [STEP 1] Staged mode — payload downloaded from C2 at runtime, [STEP 1/3] Scheduled Task (DotNetSvcUpdateTask, logon + every 3 min), [STEP 2/3] Boot Task (DotNetSvcCoreTask, INTERACTIVE_TOKEN + BootTrigger), [UNINSTALL 4/6] Removing persist_loader DLL + registry PE data..., [REPAIR] Reinstalling boot task (INTERACTIVE_TOKEN)....
  • ENTER/DONE 함수 추적: "[HEIS] encrypt_text_only ENTER" / "[HEIS] encrypt_text_only DONE", "KeylogResolveAPIs: ENTER". 새로운 함수를 생성할 때 진단형 LLM이 기본적으로 사용하는 방식.
  • 상세한 진단 정보: "FindHostProcessEx: scan stats: total=%lu sessSkip=%lu openFail=%lu native=%lu wow64=%lu mapReject=%lu dbgReject=%lu sess=%lu", "ManualMap: thread hijacked and resumed — DLL injection via thread hijack complete". 말이 필요 없을 정도로 명확한 출력 결과로, 악성코드치고는 유난히 말이 많다.
  • C 문자열의 긴 대시: "elevate: FAIL — no deployed DLL path", ">>> .elevate: NOT proxy — spawning trusted host to handle elevation".

실행 체인

MainEntryLogic C2 루프로 진입하기 전에 전체 초기화 순서를 실행하는 오케스트레이션 함수는 다음과 같습니다:

start
 └─ WinMain
     └─ MainEntryLogic
         1. DynInit                // Bootstrap API resolution
         2. ElevationStateCheck    // ".elevate" marker detection, routes by token elevation state
         3. SingleInstanceCheck    // XOR-decrypted mutex, exit if already running
         4. EvasionInit            // Direct syscalls + ETW HWBP
         5. SyscallResolverInit    // CPUID + hash-based kernel32 resolution
         6. SleepMaskInit          // Sleep obfuscation setup
         7. ComputeMachineID       // DJB2(module name) ^ volume serial
         8. IsRunningHollowed      // Process hollowing self-check
         9. CollectSysInfo         // CPU, GPU, RAM, OS, AV, apps
        10. FilelessPersist        // Drop stub DLL, registry artifact
        11. InstallPersistence     // Three scheduled tasks via COM ITaskService
        12. C2Loop_Init → C2Loop_Main

시작 시, 임플란트는 사용자 이름과 컴퓨터 이름을 DJB2 해시 처리한 후 미리 계산된 테이블에서 각각을 조회합니다. 일치 항목이 프로세스에서 제거됩니다. 공개된 샌드박스 우회 단어 목록을 사용하여 테이블에 무차별 대입 공격을 수행한 결과, 61 의 항목 중 20 가 복구되었습니다. 여기에는 WDAGUtilityAccount (Windows Defender Application Guard), 여러 DESKTOP-XXXXXXX 기본 VM 이름, 그리고 Joe Sandbox 페르소나 (abby, patex, george, john, lisa, frank, RDhJ0CNFevzX)가 포함됩니다.

방어 회피

직접 시스템 호출 및 API 래퍼

PHANTOMPULSE는 DJB2 해시를 사용하여 PEB→Ldr 를 순회하며 ntdll 함수를 식별하고, 각 NT 함수의 프롤로그에서 시스템 서비스 번호(SSN)를 추출한 뒤, 전용 시스템 호출 스텁을 생성합니다. 이러한 스텁은 임플란트의 나머지 부분에서 전반적으로 사용되는 상위 수준의 헬퍼 함수들로 감싸져 있습니다:

  • NtCreateFile_Wrap
  • NtWriteFile_Wrap
  • NtClose_Wrap
  • NtCreateSection_Wrap
  • NtMapViewOfSection_Wrap
  • NtProtectVirtualMemory_Wrap
  • NtWriteVirtualMemory_Wrap

임플란트의 나머지 부분은 kernel32/ntdll 에 노출된 함수 대신 이러한 래퍼 함수를 호출함으로써, EDR 제품이 문서화된 API 표면에 삽입하는 사용자 모드 ntdll 후크(IAT 대체, 인라인 우회, 트램폴린 패치 등)를 무력화합니다.

단 하나의 헬퍼 함수가 모든 디스크 쓰기 작업을 NtCreateFile + NtWriteFile 로 직접 라우팅하며, 액세스 오류 발생 시 삭제 후 재시도 방식을 적용합니다.

문자열 및 구성 정보 난독화

PHANTOMPULSE는 다양한 아티팩트를 처리하기 위해 4개의 XOR 레이어를 사용합니다:

무엇Key열쇠가 있는 곳
C2 대체 URL, 뮤텍스, 드롭 경로 파일 이름16바이트: F7 7C 8E 40 DF C1 7B E5 E7 4D 86 79 D5 B3 53 41~에 내장된 .rdata
블록체인 제공업체 호스트 이름 (UTF-16 LE)8바이트: 5A 3C 7E 1D 9F 2B 4E 8A~에 내장된 .rdata
COM Elevation Moniker, 키로거 파일 페이로드0xE95CA237, 상수를 제외하기 위해 런타임에 계산되며 .rdata계산된 값이며, 저장된 값이 아님
블록체인 거래 내역에서 추출한 C2 URL input해결기 지갑 주소 자체공개 조회 키에서 재사용됨

AMSI, WLDP 및 ETW는 하드웨어 중단점을 우회합니다

PHANTOMPULSE는 단일 공유 기본 요소, 즉 각 API 진입점에 설정된 하드웨어 중단점을 통해 AMSI, Windows 잠금 정책 코드 신뢰도 검사 및 ETW 원격 측정 기능을 비활성화합니다. 이 중단점은 인라인 패치 없이 반환 값을 위조하는 벡터화된 예외 처리기에 의해 가로채어집니다.

슬롯대상 API위조 반품 (RAX)
DR0WldpQueryDynamicCodeTrust0 (S_OK)
DR1AmsiScanBuffer0x80070057 (E_INVALIDARG)
DR2EtwEventWrite0 (STATUS_SUCCESS)

단계별 작동 원리:

  1. 이 임플란트는 대상 API를 해결합니다. AMSI와 WLDP는 ` LoadLibraryA `를 거쳐 해시 기반 내보내기 조회를 수행하며, ETW는 ntdll이 이미 로드되어 있으므로 PEB→Ldr 탐색을 사용합니다.
  2. HWBP 디스크립터(대상 API 주소, 모드, 위조된 반환값)는 전역 슬롯 테이블에 있는 4개의 40바이트 슬롯 중 하나에 기록됩니다.
  3. 헬퍼 스레드는 대상 스레드를 일시 중지한 후, ` NtGetContextThread ` 또는 ` NtSetContextThread `를 호출하여 DR0–DR3 + DR7을 기록한 다음, 다시 실행을 재개합니다. (임플란트의 벡터화된 예외 핸들러가 이미 설치되어 있는 경우, 대신 인-프로세스 STATUS_BREAKPOINT 예외가 발생하며, 이를 통해 VEH는 헬퍼 스레드 없이 슬롯 테이블을 읽고 DR을 프로그래밍할 수 있습니다.)
  4. 보호된 API가 호출되면, CPU는 해당 함수의 첫 번째 명령어에서 ` Debug Exception ` 예외를 발생시킵니다.
  5. 이식된 모듈의 벡터화된 예외 핸들러는 ` Debug Exception`를 가로채고, 4슬롯 테이블을 순회하여 호출 주소를 찾아낸 뒤 스레드 컨텍스트를 수정합니다. 즉, ` CONTEXT.Rax `는 슬롯별로 위조된 반환값으로 설정되고, ` CONTEXT.Rip `는 호출자에게 반환하는 미리 저장된 ` "` 스킵 `" ` 썸크로 리디렉션됩니다.
  6. 이 핸들러는 ` EXCEPTION_CONTINUE_EXECUTION`를 반환합니다. 발신자는 API가 실행된 것처럼 위조된 RAX를 확인하게 됩니다.

디스패처는 두 가지 경로를 처리합니다. 하나의 핸들러(VEH_Dispatcher)는 임플란트 자체의 ` RaiseException(STATUS_BREAKPOINT) ` 호출(슬롯 테이블을 기반으로 DR 레지스터를 초기화하거나 재프로그래밍하는 데 사용됨)과 보호된 API가 호출될 때 발생하는 ` STATUS_SINGLE_STEP ` 예외를 모두 처리합니다. 예외 코드가 분기를 결정합니다. STATUS_BREAKPOINT 는 DR 프로그래밍을 트리거하고, STATUS_SINGLE_STEP 는 스푸핑을 트리거합니다.

핸들러 또한 직접 등록되지 않습니다. AddVectoredExceptionHandler 런타임에 새로 할당된 MEM_PRIVATE 페이지 내에서 할당된 작은 JMP 썽크를 수신합니다(VirtualAlloc + VirtualProtect 에서 PAGE_EXECUTE_READ 로). 이 썽(thunk)은 6바이트 명령어 코드( FF 25 00 00 00 00)로 구성된 간접 점프( JMP [RIP-relative] )이며, 그 뒤에 디스패처의 주소가 인라인으로 이어집니다. AmsiScanBuffer, WldpQueryDynamicCodeTrust 또는 EtwEventWrite 에는 바이트 단위의 데이터가 전혀 기록되지 않기 때문에, 프롤로그 패치를 스캔하는 서명 기반 탐지 방식은 이를 완전히 놓치고 맙니다.

빌드 변형: 활성 및 비활성 하위 시스템

이 바이너리 파일에는 코드나 문자열 형태로 여러 하위 시스템이 포함되어 있지만, 이번 빌드에서는 활성화되어 있지 않습니다. 이것은 더 방대한 코드베이스를 간소화한 버전입니다.

  • NTDLL 언후킹: 언후킹 하위 시스템에 대한 디버그 문자열은 .rdata (UnhookNtdll: ntdll base = %p, applied %d relocation fixups to .text)에 존재하지만, 이를 참조하는 코드가 없습니다. 이 변형에서는 작동하지 않습니다.
  • 레지스트리에 상주하는 PE 블롭 로더: 이전 빌드에서는 다음 단계 PE 파일을 레지스트리에 저장했습니다. 이 빌드에서는 그렇지 않지만, 제거 루틴은 여전히 기존 레지스트리 항목을 정리합니다.
  • COM 하이재킹 지속성: 이 빌드에서는 절대 설치되지 않습니다. 이에 대한 정리 로직은 제거 루틴에 남아 있습니다.
  • 프린트 모니터의 지속성: COM 하이재킹과 동일한 패턴; 설치 경로는 없지만 제거 경로는 유지됨.

마지막 세 가지(레지스트리 BLOB 로더, COM 하이재킹, 프린트 모니터)는 정반대의 양상을 보입니다. 즉, 설치 로직은 없고 정리 로직만 포함되어 있으며, 이는 이전 배포 방식과의 하위 호환성을 위해 유지된 것입니다.

기능페이로드 빌드증거
직접 시스템 호출 (SSN 추출)활성SSN 추출 및 스텁 생성 확인
AMSI / WLDP / ETW HWBP 우회활성DR0/DR1/DR2 공유 헬퍼 스레드 프리미티브를 통해
3단계 공정 주입활성PhantomInject, DbgNexum, ManualMap 모두 정상적으로 작동합니다
블록체인 C2 해결 방안활성세 곳의 블록스카우트 제공업체에 문의함
NTDLL 언훅킹데드 코드문자열이 존재하지만 코드 참조는 없음
HEIS 암호화비활성화됨암호화/복호화 스텁 구현
레지스트리에 상주하는 PE 블롭 로더구버전 전용제거 시에만 정리됩니다
COM 하이재킹 지속성구버전 전용제거 과정에서 삭제되었으며, 설치된 적이 없습니다
모니터 지속성 인쇄구버전 전용제거 과정에서 삭제되었으며, 설치된 적이 없습니다
미끼 문자열활성다음에 참조되지 않은 미끼 문자열 4개가 있습니다 .rdata

명령 및 제어

블록체인 C2 해결 방안

PHANTOMPULSE는 세 개의 Blockscout 제공업체를 통해 C2 조회 기능을 분산화합니다:

  • eth.blockscout[.]com (이더리움 L1)
  • base.blockscout[.]com (기본 L2)
  • optimism.blockscout[.]com (낙관주의 L2)

지갑 주소 0xc117688c530b660e15085bF3A2B664117d8672aA 는 16바이트 키를 사용하여 스토리지에서 XOR 복호화되었습니다. 임플란트는 각 공급자에 대해 HTTPS GET 요청(포트 443, SSL 인증서 오류 무시)을 전송하고, 최신 거래의 input 필드를 가져온 다음 이를 16진수 디코딩하고, 지갑 주소 바이트를 키로 사용하여 XOR 복호화한 후, 결과가 http 로 시작하는지 확인합니다. 모든 방법이 실패할 경우, 하드코딩된 URL인 https://panel.fefea22134[.]net 로 자동 전환됩니다.

리졸버는 트랜잭션의 발신자를 확인하지 않습니다. 이 코드는 가장 최근에 디코딩된 ` input ` 값이 ` http`로 시작하는지 확인하기만 합니다. 누구나 지갑 바이트 데이터 아래에 자신의 URL을 XOR 인코딩하여 해당 지갑으로 트랜잭션을 제출할 수 있으며, 이후 폴링을 수행하는 해당 캠페인의 모든 PHANTOMPULSE 인스턴스는 해당 URL로 해결됩니다. 네트워크 보안 담당자들에게 있어, 이는 단 한 번의 트랜잭션 비용만으로 구현 가능한 효과적인 싱크홀입니다.

엔드포인트 및 하트비트

실행 시점에 5개의 API 경로가 생성되며, 세션별 키를 사용하여 메모리 내에서 재암호화됩니다:

경로메서드콘텐츠 유형목적
/v1/telemetry/reportPOSTapplication/json전체 시스템 원격 측정 기능을 갖춘 하트비트
/v1/telemetry/tasks/<machine_id>get명령 가져오기
/v1/telemetry/upload/POSTimage/bmp스크린샷 / 파일 업로드
/v1/telemetry/resultPOSTapplication/json명령 결과 전달
/v1/telemetry/keylog/POSTtext/plain키로그 데이터 업로드

heartbeat는 전체 시스템 프로필을 JSON 형식으로 전송합니다:

{
  "machine_id": "<uint32>",
  "status": "online",
  "cpu": "<model>",
  "gpu": "<description>",
  "ram_mb": "<uint32>",
  "os": "<version>",
  "username": "<user>",
  "computer_name": "<name>",
  "cores": "<uint32>",
  "screen_w": "<int>",
  "screen_h": "<int>",
  "privilege": "<user|admin|admin_nouac|system>",
  "build": "payloads",
  "public_ip": "<ip>",
  "av_list": ["<av1>", "<av2>"],
  "apps": ["<app1>", "<app2>"],
  "last_cmd": "<cmd>",
  "last_cmd_result": "<result>"
}

두 개의 응답 필드가 분석됩니다: "status":"deleted" 는 완전한 제거를 트리거하고, "ip":"<value>" 는 공용 IP 캐시를 채우지만, 이는 로컬 검색(ipif[.]org)이 / icanhazip[.]com/ checkip.amazonaws[.]com) 아직 채워지지 않았다면.

반복 리듬과 회복력

  • 수면: [20, 40]초 범위 내에서 균일한 난수
  • 자가 복구: 2번째 반복에서 실행되며, 이후 10회마다 실행됩니다
  • 상태 모니터링 티크: 임플란트가 가동된 후 처음 한 번 호출하고, 그 후에는 5회마다 호출합니다. 로컬 시스템 정보 구조체(CPU%, RAM, OS 버전, 가동 시간, 컴퓨터 이름)를 채웁니다.
  • 오류 임계값: 10 연속적인 하트비트 실패가 발생하면 중단된 SSL/TLS 복구 프로세스가 자동 재시작됩니다.
  • 재해결: 오류 발생 시 블록체인 재해결이 실행되며, 해결된 URL이 변경되면 오류 카운터가 초기화됩니다.
  • 공용 IP: api4.ipify[.]orgipv4.icanhazip[.]comcheckip.amazonaws[.]com
  • 연결 상태 확인: microsoft[.]com, google[.]com, cloudflare[.]com, github[.]com

명령 디스패치

명령 디스패처는 DJB2 해시를 기반으로 명령을 라우팅합니다. 총 8가지 명령어:

해시명령행동
0x04CF1142inject셸코드/DLL/EXE를 주입합니다. 유형별 경로: 셸코드 → PhantomInject; DLL → ManualMap; EXE → DbgNexum. AMSI 및 WLDP HWBP 우회 기능은 첫 번째 inject 호출에 설치됩니다(ETW HWBP는 이미 EvasionInit 에서 적용된 상태입니다).
0x7C95D91Adrop파일을 디스크에 드래그 앤 드롭한 다음 실행하세요. DLL, EXE, 셸코드(APC 주입) 및 MSI 페이로드를 지원합니다.
0x9A37F083screenshotGDI로 캡처한 후 너비를 960px로 축소하고 BMP 형식으로 업로드합니다.
0x08DEDEF0keylog인라인 키로거를 시작하거나 중지합니다.
0x4EE251FFuninstall6단계 정리 및 종료.
0x65CCC50Belevateschuac 기법을 통한 UAC 우회 (IElevatedFactoryServer::ServerCreateElevatedObject(CLSID_TaskScheduler)); 임플란트를 재실행하는 일시적인 권한 상승 태스크를 등록합니다.
0xB3B5B880downgrade시스템 → 관리자 권한 상승 전환.
0x20CE3BC8(자동 재시작)계단식 자체 종료: 먼저 NtTerminateProcess(-1, 0) 시스템 호출을 시도하고, 해결되지 않으면 ExitProcess(0) 로 전환합니다. Persistence는 다음 예정된 작업 틱에서 임플란트를 다시 시작합니다. 작동 방식은 소프트 재시작과 동일합니다.

여덟 번째 핸들러에는 해당 핸들러의 이름을 명시한 디버그 로그가 없습니다. 이 작업은 자동으로 종료되며, 예약된 작업이 다음 틱에 임플란트를 다시 시작합니다. LLM 스타일의 보조 코드(디버그 문자열)가 없다는 점 때문에, 이 핸들러는 바이너리 내에서도 LLM이 생성한 것이 아니라 개발자가 직접 추가한 것으로 보이는 몇 안 되는 사례 중 하나입니다.

주사 기법

PHANTOMPULSE는 페이로드 유형별로 각각 하나씩, 총 세 가지 주입 기술을 제공합니다. inject 의 C2 명령어는 쉘코드를 PhantomInject 로, DLL 파일을 ManualMap 로, EXE 파일을 DbgNexum 로 전송합니다.

AMSI/WLDP 하드웨어 브레이크포인트 우회 기능은 인젝터가 실행되기 전, 첫 번째 ` inject ` 호출 시점에 설치됩니다.

페이로드 유형.인젝터전략
ShellcodePhantomInjectdbghelp.dll 에서 모듈 스톰핑을 통해 SEC_IMAGE
EXEDbgNexum디버그 API 상태 기계
DLLManualMapPE 매뉴얼 매핑 전체

PhantomInject: dbghelp.dll에 모듈 덮어쓰기

모듈 스톰핑은 정상적인 Windows DLL을 SEC_IMAGE 로 매핑하고 .text 을 덮어씌움으로써 MEM_PRIVATE 의 할당을 우회합니다:

  1. SeDebugPrivilege 를 가져온 다음(출처: OpenProcessToken / LookupPrivilegeValueW / AdjustTokenPrivileges), 일곱 개의 호스트 프로세스 후보 중 하나에 대해 프로세스 스냅샷을 순차적으로 검사합니다(대소문자 구분 없이 일치). 우선순위 순으로 시도해 본 사이트: sihost.exe, taskhostw.exe, backgroundTaskHost.exe, RuntimeBroker.exe, dllhost.exe, ctfmon.exe, explorer.exe.

  2. NtOpenFile 를 통해 dbghelp.dll 를 열고, SEC_IMAGE 섹션을 생성한 다음, 다음을 통해 대상에 매핑합니다. NtMapViewOfSection

  3. .text 의 RVA 및 크기에 대한 로컬 복사본을 분석한 후, 이를 해제합니다

  4. 스레드를 선택하고 일시 중지하며, 컨텍스트를 캡처합니다

  5. 82바이트 크기의 저장-호출-복원 트램폴린을 생성합니다

  6. 매핑된 DLL의 ` .text `에 셸코드 및 트램폴린을 기록합니다

  7. 보호 기능을 PAGE_EXECUTE_READ

  8. 트램펄린으로 다시 연결하고, 스레드를 재개합니다

메모리 스캐너의 관점에서 볼 때, 이 결과는 올바른 파일 경로, 섹션 이름 및 첫 페이지 해시를 갖춘 파일 기반 이미지 영역인 정당한 ` dbghelp.dll` 내에서 스레드가 실행되고 있는 것처럼 보입니다.

DbgNexum: 실행 제어기 역할을 하는 디버그 API

DbgNexum은 EXE 페이로드를 처리합니다. 타깃에 실행 가능한 코드를 미리 작성하는 대신, Windows 디버그 API를 사용하여 예외가 발생할 때마다 실행을 진행합니다. 즉, 가젯이 타깃 내의 완전한 Windows API로 구성된 ROP 체인입니다.

이 기술은 PHANTOMPULSE가 독자적으로 개발한 것이 아닙니다. 이는 dis0rder0x00/DbgNexum2026년 1월 4일 GitHub에 공개된 공개 개념 증명(PoC)입니다. 운영자는 임플란트의 디버그 문자열에 공개된 기술 이름("DbgNexum")을 그대로 유지했으며, 내부 상태 머신도 1:1로 일치합니다. 즉, 동일한 베이트 API, 섹션 이름, 가젯 체인 및 상수를 사용하고 있습니다. PHANTOMPULSE는 리프트된 x64 코어를 PoC에는 없는 운영 지원 구조로 감싸고 있습니다. 여기에는 호스트 프로세스 선택(FindHostProcessEx), 새로운 SysWOW64\cmd.exe / rundll32.exe / notepad.exe 을 생성하는 폴백, 맞춤형 PE 로딩 부트스트랩(원시 셸코드 대신 완전한 EXE 파일을 전달할 수 있도록), 그리고 별도의 WoW64 크로스 아키텍처 변종.

네이티브 x64 페이로드의 경우, 임플란트는 PE 파일, 부트스트랩 스텁 및 트램폴린 구성을 지정된 파일 매핑 섹션 내에 미리 배치합니다. 섹션 이름은 리터럴 2바이트 문자열 "MZ" 이며, 임플란트는 DebugActiveProcess 에 연결되고 FileTimeToSystemTime 에서 DR0에 하드웨어 중단점을 설정하여 원격 스레드를 생성합니다.

미끼가 브레이크포인트에 도달하면, 상태 머신이 이 API 체인을 통해 타깃을 처리합니다:

  1. RIP 을 트랩 플래그가 설정된 상태로 DbgBreakPoint+1 으로 리디렉션하면, 발생된 단일 단계 예외가 체인의 나머지 부분으로 이어집니다.
  2. LocalAlloc(LMEM_ZEROINIT, 3), 3바이트 이름 버퍼를 할당합니다.
  3. memcpy(buf, kernel32_base, 2), kernel32.dll 의 DOS 헤더에서 "MZ" 를 복사하여 버퍼에 저장합니다.
  4. memset(stack+40, 0, 8): 스택 인자 슬롯을 초기화합니다.
  5. OpenFileMappingA(0x1F, FALSE, "MZ"), 전체 단면 매핑 권한으로 준비된 단면을 엽니다.
  6. MapViewOfFile(...), 이를 대상에 매핑합니다.
  7. RIP 을 부트스트랩 스텁인 mapped_base + 0x400 으로 리디렉션합니다. 다음은 직접 기록된 유일한 단계입니다: DbgNexumLoop64: stage 6 -> stub at %llx, base=%llx. (PoC는 원시 셸코드를 위해 mapped_base + 0 로 리디렉션되며, PHANTOMPULSE는 사용자 정의 PE 로더로 이동하기 위해 +0x400 오프셋을 추가합니다.)

각 전환 단계에서는 다음 디버그 이벤트를 가로채고, ` RSP`를 복원하며, 트랩 플래그를 지우고, 다음 호출을 위해 ` RIP ` 및 인자 레지스터(`RCX`, ` RDX`, ` R8`, ` R9`)를 수정한 후, 디버그 대상 프로세스를 계속 실행합니다. DR0는 저장된 각 반환 주소에서 실행형 하드웨어 중단점으로 재사용되므로, 대상 시스템에 대한 인라인 패치나 WriteProcessMemory 가 필요하지 않습니다. 커널의 관점에서 보면, 단순히 kernel32.dll 내부의 한 스레드가 LocalAlloc, OpenFileMappingA, MapViewOfFile 을 호출한 것뿐입니다.

크로스 아키텍처 경로(64비트 임플란트에서 PE32로 전송)는 공개된 PoC에는 없는 PHANTOMPULSE 전용 변형입니다. 이 방법은 지름길을 이용합니다. 임플란트는 NtReadVirtualMemory 를 통해 대상의 PEB( PEB.Ldr )를 탐색하고( ProcessWow64Information 를 사용하여 32비트 또는 64비트 PEB 레이아웃을 선택), 호출 가능한 진입점을 가진 DLL을 찾은 다음, NtCreateSectionNtMapViewOfSection 를 통해 32비트 로더 스텁과 트램폴린이 포함된 섹션을 두 프로세스 모두에 미리 매핑합니다. 리디렉션은 단 두 단계로 이루어집니다. 먼저 RtlExitUserThread 에서 미끼를 제공한 다음, DbgBreakPoint를 통해 RIP 로 한 번에 이동하여 트램폴린으로 연결됩니다. 이 경로에는 API 체인이 없습니다. 해당 섹션이 양쪽 모두에 이미 매핑되어 있으므로 OpenFileMappingA/MapViewOfFile 는 필요하지 않습니다.

크로스 아치 호스트 선택은 ` FindHostProcessEx`를 통해 수행되며, 이 과정에서는 중요한 시스템 프로세스(`csrss.exe`, ` lsass.exe`, ` smss.exe`, ` winlogon.exe`, ` services.exe`, ` wininit.exe`, ` svchost.exe`, ` MsMpEng.exe`)를 제외합니다. 또한 사용 가능한 WoW64 호스트가 발견되지 않을 경우, ` SysWOW64\cmd.exe `, ` rundll32.exe ` 또는 ` notepad.exe `를 새로 생성하여 대체합니다.

ManualMap: 전체 PE 매퍼

ManualMap은 완벽한 PE 수동 매핑 구현을 통해 DLL 페이로드를 처리합니다:

  1. MZ/PE 시그니처를 검증하고, x64 호스트 경로에서 PE32를 거부합니다(디버그 로그: "PE32 DLL in x64 host is impossible")

  2. 다음 방법을 통해 대상에 SizeOfImage 를 할당합니다. NtAllocateVirtualMemory

  3. 헤더와 섹션을 로컬 스테이징 버퍼로 복사합니다

  4. 기본 재배치 적용 (IMAGE_REL_BASED_DIR64, IMAGE_REL_BASED_HIGHLOW)

  5. LoadLibraryA 를 통해 가져오기 문제를 해결합니다 + GetProcAddress

  6. PE 헤더를 지웁니다( SizeOfHeaders 바이트를 0으로 채움).

  7. 준비된 이미지를 원격 할당 영역에 기록합니다

  8. 섹션별 메모리 보호 설정

  9. 0x2000바이트 크기의 별도 원격 할당 영역( PAGE_EXECUTE_READ 로 설정됨)에 137바이트 크기의 트램폴린을 생성합니다:

트램펄린 셸코드 전체를 담은 Gist에는 전체 바이트 데이터가 포함되어 있습니다.

10. suspend / get-context / set-context를 통해 스레드를 가로챈다

권한 확대

elevate 명령어는 schuac 기법 (IElevatedFactoryServer::ServerCreateElevatedObject(CLSID_TaskScheduler))을 통해 UAC를 우회하는 것으로, zcgonvh에 의해 UACME 이슈 #129로 공개되었으며, 현재 ID 74로 등록되어 있습니다.

원리

MaintenanceUI.dllCMaintenanceUIVirtualFactory (CLSID {A6BFEA43-501F-456F-A845-983D3AD7B8F0})는 Elevation 레지스트리 키에 등록되어 있으므로, 운영 체제는 관리자 권한이 없는 호출자에게 관리자 권한이 부여된 인스턴스를 제공합니다. IElevatedFactoryServer 인터페이스는 ServerCreateElevatedObject(rclsid, riid, ppv) 메서드를 제공하며, 권한이 상승된 서버는 이 메서드를 사용하여 권한이 상승된 컨텍스트 하에서 다른 CLSID를 인스턴스화합니다. PHANTOMPULSE는 CLSID_TaskScheduler 에 요청을 보내고, 반환된 ITaskService 에 접속한 후, 해당 서비스를 이용해 임플란트를 재시작하는 HighestAvailable-RunLevel 태스크를 등록합니다.

elevate 의 C2 명령어

ProcessCommands 내부의 elevate 핸들러:

  1. 작업 액션을 생성합니다. 명령어는 ` <system_dir>\rundll32.exe `이며, 인수는 ` \"<deployed_dll>\",DllRegisterServer`입니다. 사용자 식별자는 GetEnvironmentVariableW 에서 확인 가능한 COMPUTERNAME\USERNAME 입니다. ![Elevate 명령어 호출][/assets/images/blockchain-c2-phantompulse-rat-sinkhole/image17.png]
  2. .elevate 마커를 인코딩된 매개변수가 아닌 단일 바이트("1", 0x31)로 기록합니다. 이 쓰기 작업은 사용자 모드 후크를 우회하기 위해 NtCreateFileNtWriteFile 를 경유합니다. 이 마커는 단순히 존재 여부를 표시하는 역할을 할 뿐이며, 고도 관련 매개변수들은 임플란트가 등록할 작업 정의 내부에 포함됩니다.
  3. 실행 시점에 COM 권한 상승 모니커를 XOR 연산으로 복호화하면, .rdata 의 66 바이트를 시드 0xE95CA237 와 XOR 연산한 결과로, 이는 Elevation:Administrator!new:{A6BFEA43-501F-456F-A845-983D3AD7B8F0} 로 해독됩니다.
  4. CoGetObject(moniker, &BIND_OPTS3{dwClassContext=CLSCTX_LOCAL_SERVER}, IID_IElevatedFactoryServer, &factory) 를 호출하여 관리자 권한이 부여된 IElevatedFactoryServer* 를 얻은 다음, factory->ServerCreateElevatedObject(CLSID_TaskScheduler, IID_ITaskService, &elevatedTaskService) 를 호출하여 관리자 권한을 상속받은 ITaskService* 를 얻습니다.
  5. 위의 rundll32 작업을 사용하여 HighestAvailable RunLevel에 일시적인 작업 DotNetSvcElevateTask 을 등록합니다.
  6. 기존의 비권한 상승 영구 작업들을 모두 삭제하여, 기존의 낮은 IL 영구 작업이 권한 상승된 재시작과 경합하지 않도록 합니다.
  7. 일시적인 작업에 대해 ` ITaskService::Run `를 호출한 후 즉시 해당 작업을 삭제합니다. 단일 인스턴스 뮤텍스를 해제하고 중간 IL 임플란트를 종료합니다.

프록시를 통한 rundll32 재시도

CoGetObject / RegisterTask 경로가 실패할 경우, 대체 경로가 작동합니다. 별도의 시작 경로를 통해 CreateProcessW 에서 직접 새로운 rundll32.exe "<deployed_dll>",DllRegisterServer 를 생성하며, 재시도 기능이 제공됩니다(">>> .elevate redirect attempt %d"). 해당 rundll32 파일 내에서 임플란트는 isProxy=1, elevated=0 을 감지하고, 세 가지 등록 변형(ELEVATED+INTERACTIVE+user, ELEVATED+INTERACTIVE, INTERACTIVE)을 사용하여 슈악(Schuac) 시퀀스를 재시도합니다. 성공하면 권한 상승된 작업이 실행되고, 권한 상승된 재시작 프로세스가 이를 이어받습니다. 프로キシ는 리소스가 소진되면 “ ">>> Phase 1: all registration methods failed, cleaning marker" ”라는 메시지를 기록하고 종료됩니다.

런들32(rundll32) 관리자 권한으로 재실행

일시적 작업이 실행되면, ` svchost.exe (Schedule) `는 새로운 고 IL 토큰을 사용하여 ` rundll32.exe "<deployed_dll>",DllRegisterServer `를 실행합니다. 임플란트의 ` DllRegisterServer ` 출력 프로세스가 엔트리 포인트로 실행됩니다. 시작 시 ` .elevate ` 마커와 높은 권한(high-IL) 토큰을 감지하면 권한 상승 경로로 라우팅됩니다:

  • .elevate 마커를 읽고 삭제합니다.
  • 관리자 권한 환경에서 지속성 기능을 다시 설치합니다. 여기에는 \Microsoft\Windows\NetFramework\ 아래의 부팅 작업 DotNetSvcCoreTask 이 포함되며, 이는 INTERACTIVE_TOKENBootTrigger 에 등록되어 있습니다. 등록 시 관리자 권한이 필요합니다.
  • 고 IL 서비스로서 임플란트의 정상적인 운영을 계속합니다.

마커 기반 라우팅

스타트업이 시작될 때마다 ` MainEntryLogic `는 ` .elevate ` 경로에서 ` GetFileAttributesW `를 호출합니다. INVALID_FILE_ATTRIBUTES 가 반환되면, 임플란트는 모든 고도 관련 로직을 건너뛰고 정상적으로 시작합니다. 마커가 존재하면, 임플란트는 두 가지 추가 정보를 수집합니다. 현재 프로세스가 rundll32.exe/regsvr32.exe 프록시인지 여부와 토큰이 관리자 권한을 가지고 있는지 여부입니다. 그런 다음 이 두 가지 정보를 조합하여 경로를 결정합니다:

마커프록시높아진처리 결과
결석해당 없음해당 없음일반적인 시작, 특별한 라우팅 없음
현재아니요아니요rundll32 프록시를 생성하여 슈악(SchuAC) 체인을 다시 시도한 후 종료합니다
현재아니요rundll32 내부에서 슈악(SCHUAC)을 재시도하고, 실패 시 마커를 삭제한 후 종료한다
현재*마커 삭제, 부팅 태스크를 통해 지속성 재설정, 높은 IL로 계속 진행

마커 파일의 내용은 절대 읽히지 않으며, 파일의 존재 여부만으로도 라우팅이 결정됩니다.

Elastic Security Labs의 ‘Windows UAC 우회 기법 탐구’는IElevatedFactoryServer ’ 유형의 우회 기법에 대한 탐지 패턴을 직접 다룹니다.

지속성 유지

예약된 작업 3개

PHANTOMPULSE는 COM ITaskService 인터페이스를 통해 세 개의 예약된 작업을 설치하며, 각 작업은 rundll32.exe "<stub_dll>",DllRegisterServer 를 실행합니다:

작업트리거간격실행 레벨
DotNetSvcUpdateTask사용자 로그인 + 시간3분스탠더드
DotNetSvcCoreTask부팅 + 시간15분사용 가능한 최대값 + 숨김
DotNetSvcUserTask사용자 로그인로그인 시스탠더드

부팅 작업은 \Microsoft\Windows\NetFramework\ 경로에 위치하여 정상적인 .NET 유지 관리 작업과 자연스럽게 통합됩니다. 전체 XML 본문은 .rdata 에 포함되어 있으며, 런타임에 생성되는 것이 아닙니다. 이 리터럴 블롭에 있는 서명은 빌드 간에도 유지됩니다.

설치 과정에서는 작업당 여러 가지 RegisterTask 대체 변형을 시도하며, INTERACTIVE_TOKENS4U 로그온 유형을 서로 다른 사용자 식별자 형식(COMPUTER\User, SID, 짧은 사용자 이름)과 조합합니다. 시도 순서는 임플란트의 권한 컨텍스트(SYSTEM, 관리자 또는 일반 사용자)에 따라 달라지며, 실패할 경우 성공할 때까지 다음 변형으로 넘어갑니다.

스텁 DLL 드롭

배포된 에이전트는 EXE 파일 내부의 ` .rdata` 폴더에 암호화 및 압축된 상태로 저장됩니다. FilelessPersist 요청이 있을 때마다 이를 복호화하고, 그 결과를 디스크에 기록합니다 (NtCreateFile + NtWriteFile).

암호 해독은 두 단계로 이루어집니다:

  1. (decoded[i] = blob[i] ^ key[i & 0xF] 에서) 회전하는 16바이트 키를 사용하여 블롭을 XOR 디코딩하되, LZNT1 청크 헤더를 초기화하는 하드코딩된 첫 번째 바이트로 decoded[0] = 0x1E 를 사용합니다.
  2. RtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, ...) 디코딩된 버퍼에 대해, 이는 DLL(svcagent.dll)입니다.

우선순위 순서대로 경로를 탐색하며, 액세스 오류가 발생하면 다음 경로로 넘어갑니다:

  1. %ProgramData%\AssetMon\svcagent.dll (1차)
  2. %APPDATA%\AssetMon\svcagent.dll
  3. %TEMP%\svcagent.dll
  4. 중복된 %ProgramData% " 슬리퍼" 의 사본이 별도의 경로에 있음

분석가는 아래 CyberChef 스크린샷에서 볼 수 있듯이, EXE 파일에서 위의 두 영역을 읽어내고 XOR 루프를 적용한 뒤, 그 결과를 RtlDecompressBuffer (또는 LZNT1 구현체)에 입력함으로써 배포된 DLL을 오프라인 환경에서 재현할 수 있습니다.

DLL 사이드로드 마이그레이션

SetupRegistryPE 내부의 한 블록( MigrateSideload 또는 MigrateLegacySideloads 디버그 문자열 접두사로 기록됨)은 실행 중인 프로세스와 해당 실행 파일 디렉터리를 열거하여 diagcore.dll 을 찾아냅니다. 해당 파일이 발견되면, ` CopyFileW`를 통해 현재 스텁으로 파일을 덮어씁니다.

자동화된 복구

자가 복구 기능은 C2 루프의 2 반복에서 실행되며, 이후에는 10회마다 반복될 때마다 실행되는데, 이는 deferred-persist 플래그가 비어 있는 경우에 한해 수행됩니다. 결제 순서:

  1. 먼저 레지스트리 영구 저장 상태를 확인하세요. CheckRegistryPersistence 블록의 맨 위에 위치합니다. 상태가 비정상이라고 보고되면, 임플란트는 즉시 FilelessPersist (스텁 DLL을 다시 복호화하고 배치)와 InstallPersistence (작업 트리거를 다시 등록)를 재실행합니다.
  2. 작업 확인. SelfHealCheckTasks 그런 다음 세 가지 지속성 작업(DotNetSvcUpdateTask, DotNetSvcCoreTask, DotNetSvcUserTask)을 확인하고 누락된 항목이 있으면 다시 설치합니다. 부팅 작업 검사는 SYSTEM 또는 관리자 컨텍스트에서만 수행되며, 권한이 없는 호출자는 이 단계를 건너뜁니다.
  3. AV 재고 갱신. DetectInstalledAV 마지막에 실행되어 운영자가 볼 수 있는 AV 제품 목록을 새로 고칩니다.

권한 제한은 강제 퇴거에 있어 중요한 요소입니다. 비특권 모드에서는 부트 태스크 검사가 건너뛰어지므로, 정리 과정에서 부트 태스크는 검사되지 않습니다. 완전한 제거를 위해서는 관리자 권한 환경에서 하나의 창에 있는 세 가지 작업과 레지스트리 아티팩트를 모두 삭제해야 합니다.

반복 기반 자가 복구 기능 외에도, 이 임플란트는 단일 플래그를 기반으로 하는 지연 지속 메커니즘을 갖추고 있습니다. C2Loop_Main 의 하트비트 성공 경로에서, 플래그가 설정된 상태에서 하트비트 성공 카운터가 1을 초과하면 임플란트는 FilelessPersist + InstallPersistence 를 다시 실행하고 플래그를 초기화합니다. 이를 통해 PHANTOMPULSE는 반복 기반 자가 복구와 다른 트리거에 의해 발동되는 두 번째 지속성 복구 경로를 갖게 됩니다.

수집

클립보드 모니터링 기능이 포함된 인라인 키로거

이 키로거는 별도의 스레드 없이 C2 루프 내에서 인라인으로 실행됩니다. 이 기능은 런타임 시점에 user32.dll 의 API를 해석합니다:

API를 이용하는 방법목적
GetAsyncKeyState경합 주
GetForegroundWindow활성 창 감지
GetWindowTextA창 제목 캡처
MapVirtualKeyA / ToUnicode주요 번역
GetClipboardSequenceNumber클립보드 변경 감지
OpenClipboard / GetClipboardData클립보드 읽기 (CF_UNICODETEXT)

로그 파일은 ' 0xE95CA237 ' 시드로 XOR 암호화됩니다. 재전송을 방지하기 위해 업로드 시 변경된 부분만 전송합니다.

스크린샷

스크린샷은 해시를 통해 해결된 GDI API를 사용합니다. 데스크톱 화면 너비가 960 픽셀을 초과할 경우, 이미지는 업로드 전에 크기가 축소됩니다. 원시 BMP 파일은 메모리에 생성된 후 ` Content-Type: image/bmp`를 통해 업로드됩니다. screenshot 의 C2 명령(해시 0x9A37F083)에 의해 필요에 따라 실행됩니다.

시스템 정찰

이식체가 수집하는 정찰 데이터:

데이터출처
CPU레지스트리: ProcessorNameString
GPU레지스트리 디스플레이 어댑터 DriverDesc (필터: "Microsoft Basic")
RAMGlobalMemoryStatusEx
OSRtlGetVersion 빌드-버전 매핑 포함 (Windows 7부터 Windows 11까지, Server 2008 부터 Server 2025까지)
사용자 이름GetUserNameW explorer.exe 토큰에서 LookupAccountSidW 으로 대체
특권토큰 승격 유형: user, admin, admin_nouac, system
AVDetectInstalledAV 실행 중인 프로세스를 약 25~30개의 안티바이러스(AV) 업체 프로세스 이름으로 구성된 고정된 목록과 대조합니다
DetectInstalledApps 선별된 19개 앱 목록을 확인합니다
방화벽 상태SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\{Domain,Standard,Public}Profile 를 호출하여 프로필별 활성화 상태를 기록합니다.
서비스서비스 열거를 통한 실행 중인 서비스 수 확인
기기 IDDJB2(모듈 이름) ^ 일련 번호
공용 IP다중 API HTTPS 체인

이 AV 탐지 목록은 유난히 광범위하여, Defender, Norton, McAfee, Avast, AVG, Avira, Bitdefender, ESET, F-Secure, G Data, Kaspersky, Panda, Sophos, Trend Micro, VIPRE와 같은 서구권 일반 소비자용 AV 제품들을 포괄합니다. Webroot, ZoneAlarm, Comodo는 물론 EDR 공급업체(CrowdStrike, SentinelOne, Cylance, Malwarebytes, HitmanPro)까지 모두 포함됩니다. 또한 이 임플란트는 안랩 V3 (한국), 치후(Qihoo) 360 / 360 토탈 시큐리티, 텐센트 QQPC (중국), 그리고 K7 컴퓨팅 (인도)을 탐지합니다. 서구 시장을 겨냥한 일반형 정보 탈취 프로그램에서 아시아 AV(안티바이러스) 프로그램이 포함된 사례는 드물며, 이는 여러 지역 시장의 피해자를 대상으로 설계된 악성코드의 특징과 일치한다.

또한 이 임플란트는 19 에 수록된 엄선된 고가치 애플리케이션 목록을 이름으로 확인하여, 일치하는 항목이 발견되면 하트비트(App detection: found %d apps)를 통해 이를 표시합니다:

카테고리타겟
암호화폐 지갑ledger, trezor, bitcoin-core, electrum, exodus, atomic, guarda
전령들telegram, discord, signal, viber, slack, whatsapp
메일 클라이언트thunderbird, outlook
2단계 인증 앱authy
파일 전송 / SSHfilezilla, winscp
게이밍steam

탐지 기능(DetectInstalledApps)은 레지스트리를 스캔하거나 프로세스를 열거하지 않습니다. 세 가지 환경 변수 경로(%LOCALAPPDATA%, %APPDATA%, %ProgramFiles(x86)%)를 확장하고, 앱별로 고정된 UTF-16 상대 경로 접미사를 연결합니다(예: \Telegram Desktop\, \Authy Desktop\, \Ledger Live\, \@trezor\trezor-suite\, \Steam\steam.exe)를 호출하고, 각 경로에서 GetFileAttributesW 를 호출합니다. 오류가 발생하지 않고 반환되었다는 것은 앱이 설치되었으며, 해당 이름이 하트비트 결과 버퍼에 기록되었음을 의미합니다.

PHANTOMPULSE 자체는 이들 중 어느 것에서도 데이터를 추출하지 않습니다. 이 목록은 후속 임무 수행을 위한 표적 정찰 자료입니다. 운영자는 ‘하트비트’를 통해 특정 피해자가 어떤 고가치 애플리케이션을 보유하고 있는지 파악하고, inject 또는 drop 를 통해 다음에 어떤 특수 페이로드를 전송할지 결정합니다.

분석된 샘플에서는 지갑, 브라우저, 메신저 또는 인증 정보 탈취 기능이 확인되지 않았으며, 표적 목록은 오로지 운영자의 의사 결정 과정에 필요한 존재 여부 확인을 위한 자료일 뿐입니다.

제거

uninstall 명령어, "status":"deleted" 의 하트비트 응답, 또는 레지스트리의 kill 플래그에 의해 실행되는 6단계 정리 절차:

단계조치
1/6HKCU 및 HKLM에 종료 플래그를 기록하고, 호스트 프로세스를 종료합니다
2/6COM+ CreateProcessW 대체 기능을 통해 모든 3 예약 작업을 제거합니다.
3/6구형 레지스트리 항목 제거: NTLoad 값, COM 하이재킹 키, 프린트 모니터 키
4월 6일스텁 DLL, 슬리퍼 로그, 레지스트리 PE 블롭, ProgramData 디렉터리를 삭제합니다
5/6디스크에서 설치 경로와 자체 경로를 삭제합니다
6/6남아 있는 healthmon.exe 및 호스팅 중인 모든 rundll32.exe 인스턴스를 종료하십시오 svcagent.dll

단계 3 에서는 레거시 지속성 기술이 드러납니다. 이 빌드에서는 설치되지 않는 COM 하이재킹 및 프린트 모니터 키에 대한 정리 로직이 포함되어 있습니다.

어트리뷰션

팬텀펄스(PHANTOMPULSE)의 작전 기법, 표적 선정 및 인프라 선택은 라자루스(Lazarus), 블루노로프(BlueNoroff), UNC5342(컨태지어스 인터뷰), APT38 등을 포함하는 북한 연계 암호화폐 표적 침투 그룹들의 특징과 일치한다. 여러 독립적인 측면이 해당 집단 감염 사례에 대한 최근 보도 내용과 일치한다.

북한의 보도 내용과 일치하는 신호들:

  • 거래 내역의 input 필드를 통해 블록체인으로 확인된 C2는, Mandiant가 DPRK Adopts EtherHiding 보고서에서 UNC5342(Contagious Interview)와 연관 지은 데드드롭 해결 패턴과 일치합니다. PHANTOMPULSE의 세부 사항(월렛 바이트 XOR, 멀티체인 Blockscout)은 1:1 지문은 아니지만, 해당 기술 분류에는 이제 DPRK 태그가 지정되었습니다.
  • 데스크톱 암호화폐 지갑 열거 세트 (,,,,,,)ledger trezorbitcoin-coreelectrumexodusatomicguarda는 북한과 연관된 것으로 지목된 macOS 대상 목록인 Unit 42의 RustDoor / Koi Stealer와 매우 유사합니다.
  • 동일한 피해자 프로필을 대상으로 하는 크로스 플랫폼 (Windows + macOS) 임플란트 (이전 REF6598 게시물에서는 0x666[.]info 에 C2가 있고 t[.]me/ax03bot 에 텔레그램 대체 경로가 있는 macOS 버전을 다룬 바 있음)는 BlueNoroff의 특징입니다.
  • Arctic Wolf의 BlueNoroff 관련보도에 따르면, 텔레그램 및 메신저 타겟팅은 특히 BlueNoroff의 전문 분야입니다.

리졸버 지갑의 알려진 평문 서명을 통해 새로운 C2 도메인 탐색

블록체인 리졸버가 사용하는 XOR 방식은 공격자가 단일 지갑뿐만 아니라 전체 체인을 대상으로 추적할 수 있는 안정적인 2바이트 서명을 유출합니다.

두 가지 사실이 결합됩니다. 모든 C2 URL은 ht ( http:// 또는 https:// 에서 유래)로 시작하며, XOR 키는 지갑의 ASCII 주소를 그대로 사용하기 때문에, 키의 첫 두 바이트는 항상 문자 그대로 0x 입니다. ht0x 를 XOR 연산하면 \x58 \x0c 가 됩니다. 어떤 체인에서든 PHANTOMPULSE 방식의 리졸버가 생성하고, 관련 지갑이 서명한 모든 암호화된 input 필드는 4자리 16진수 문자열인 580c 로 시작합니다.

이를 통해 수색 작업이 특정 지갑을 감시하는 것에서 체인 전체를 훑어 서명을 찾는 방식으로 전환됩니다. 이더리움 메인넷, Base, Optimism의 공개 거래 데이터는 BigQuery, Dune 또는 전체 아카이브 노드를 통해 조회할 수 있습니다. 최근 블록 타임스탬프 범위로 제한하여 0x580c 로 시작하는 input 값에 대해 이더리움 공개 거래 데이터셋을 쿼리한 결과, 동일한 코드베이스에서 사용된 이전에 알려지지 않았던 리졸버 지갑들이 확인되었습니다. 각 일치는 발신자 지갑의 ASCII 주소를 키로 사용하여 디코딩함으로써 검증됩니다. 실제 C2 URL은 디코딩 후 http 로 시작합니다. 다음 CyberChef 레시피를 사용하여 C2 URL을 해독할 수 있습니다.

SELECT 
    block_timestamp AS block_time, 
    from_address AS `from`, 
    to_address AS `to`, 
    input AS data
FROM `bigquery-public-data.crypto_ethereum.transactions`
WHERE block_timestamp >= '2026-04-01 00:00:00'
  AND input LIKE '0x580c%'
ORDER BY block_timestamp DESC
LIMIT 10000;

아래 스크린샷에서 볼 수 있듯이, CyberChef는 입력 데이터를 해독하여 도메인을 확인할 수 있습니다.

결론

PHANTOMPULSE는 공개된 구성 요소들을 기반으로 설계되었습니다: 모듈 스톰핑, 디버그 API 상태 머신, 수동 매핑, 하드웨어 브레이크포인트 AMSI/WLDP/ETW 우회, 스케줄링된 작업의 지속성 유지, 그리고 블록체인 C2. 이러한 조합과 이를 하나로 묶어주는 견고함은 활발히 개발 중인 성숙한 코드베이스를 보여줍니다. 이 지속 신호는 행동 기반 신호이며, REF6598에 대한 Elastic의 행동 기반 보호 기능의 적용을 받습니다.

PHANTOMPULSE 및 MITRE ATT&CK

Elastic은 MITRE ATT&CK 프레임워크를 사용하여 지능형 지속적 위협이 기업 네트워크에 대해 사용하는 일반적인 전술, 기술 및 절차를 문서화합니다.

전술

전술은 기술이나 세부 기술의 ‘이유’를 설명합니다. 이는 적의 전술적 목표이며, 즉 특정 행동을 수행하는 이유입니다.

기술

기술은 공격자가 행동을 수행하여 전술적 목표를 달성하는 방법을 나타냅니다.

정화

YARA

Elastic Security는 이 활동을 식별하기 위해 YARA 규칙을 만들었습니다.

관찰

관측 가능합니다.유형이름참조
33dacf9f854f636216e5062ca252df8e5bed652efd78b86512f5b868b11ee70fSHA-256팬텀펄스 쥐Final payload
70bbb38b70fd836d66e8166ec27be9aa8535b3876596fc80c45e3de4ce327980SHA-256syncobs.exe팬텀풀 로더
def66275fa3baffb16e6e4ae0297861d9790ae7161fbc271a2ba05d121f13c70SHA-256비콘으로 이동GTESTIC_WIN 체크인
panel.fefea22134[.]net도메인C2 패널PHANTOMPULSE 하드코딩된 대체 처리
fea22134[.]net도메인C2 domain이진 코드로 암호화됨
195.3.222[.]251IPv4-addr스테이징 서버PowerShell/로더 배포
0xc117688c530b660e15085bF3A2B664117d8672aA암호화폐 지갑블록체인 C2 지갑ETH/Base/Optimism
0x38796B8479fDAE0A72e5E7e326c87a637D0Cbc0E암호화폐 지갑지갑 자금 조달C2 결의안 자금 지원
eth.blockscout[.]com도메인블록체인 서비스 제공업체C2 URL 확인
base.blockscout[.]com도메인블록체인 서비스 제공업체C2 URL 확인
optimism.blockscout[.]com도메인블록체인 서비스 제공업체C2 URL 확인
hVNBUORXNiFLhYYhmutex단일 인스턴스XOR 복호화됨
svcagent.dll파일 이름스텁 DLL지속성 페이로드
AssetMon디렉터리스텁 DLL 디렉터리%ProgramData% 또는 %APPDATA%
healthmon.exe파일 이름드롭퍼원본 실행 파일 이름
diagcore.dll파일 이름기존 사이드로드 DLLMigrateSideload에 의해 마이그레이션됨
.elevate파일 이름고도 표지‘Routes’의 재출시
DotNetSvcUpdateTask예약된 작업1차 지속성3분 간격
DotNetSvcCoreTask예약된 작업시스템 지속성15분, 비공개
DotNetSvcUserTask예약된 작업사용자 데이터 저장로그온 트리거
EdgeWebViewUpdateTask예약된 작업기존 작업제거 과정에서 정리됨
\Microsoft\Windows\NetFramework\DotNetSvcCoreTask작업-URI부팅 작업 경로숨겨진 예약 작업
Elevation:Administrator!new:{A6BFEA43-501F-456F-A845-983D3AD7B8F0}com-모니커UAC 우회ITaskService의 인스턴스 생성
0x666[.]info도메인macOS C2macOS 드로퍼
t[.]me/ax03botURL.텔레그램 대체 수단macOS C2 데드드롭
thoroughly-publisher-troy-clara[.]trycloudflare[.]com도메인이전 C2Cloudflare Tunnel

참고 자료

본 분석에서 인용된 기존 보고서 및 도구 모음:

이 문서 공유하기