notes.
Kembali ke semua catatan
Digital Forensics

Part 3: Interactive In-Memory Threat Graph Notebook — Hands-on Triase Ransomware & Visualisasi Vis.js

Implementasi kode end-to-end ekstraksi artefak Volatility 3 (Conti & LockBit 3.0), konstruksi NetworkX, skoring kuantitatif, dan visualisasi graf interaktif.

19 Oktober 2026
•
21 menit baca
1 pembaca

📚 Bagian 3 dari Seri Riset Digital Forensik Memori Pascasarjana (S-2)
Notebook ini merupakan kelanjutan hands-on dari kajian di Part 1: Bedah Kritis volGPT dan Part 2: Arsitektur On-Premise Threat Graph.

AUTOMATED IN-MEMORY THREAT CORRELATION PIPELINE & INTERACTIVE THREAT GRAPH

Framework Forensik Memori Volatile Terotomasi dengan Korelasi Lintas-Artifak dan Pemetaan Taksonomi MITRE ATT&CK

  • Program Studi: Magister Teknik Informatika (S-2) - Universitas Pamulang
  • Mata Kuliah: Sistem Komputer dan Jaringan / Digital Forensik
  • Dosen Pengampu: Dr. Arya Adhyaksa Waskita, S.Si., M.Si.
  • Penyusun: Raihanudin Rafif
  • Rujukan Akademis Utama: Oh et al. (2024), "volGPT: Evaluation on triaging ransomware process in memory forensics with Large Language Model", Elsevier - Forensic Science International: Digital Investigation (Scopus Q1).

Alur Pipeline Forensik Hulu ke Hilir (End-to-End): #

  1. Tahap 1: Akuisisi & Ekstraksi Artefak Memori Kernel Windows (Volatility 3 CLI: pslist, malfind, netscan, handles, cmdline).
  2. Tahap 2: Konstruksi Heterogeneous In-Memory Threat Graph menggunakan pustaka NetworkX (G=(V,E)G = (V, E)).
  3. Tahap 3: Mesin Inferensi Deterministik Pemetaan Taksonomi MITRE ATT&CK Enterprise (T1055, T1036, T1071, T1486, T1490).
  4. Tahap 4: Formulasi Kuantitatif Composite Threat Score Terbobot (Sthreat∈[0,100]S_{threat} \in [0, 100]).
  5. Tahap 5: Visualisasi Interaktif Laporan Triase Langsung di Google Colab (Vis.js Network Engine).

1. Pemasangan Pustaka Dependensi #

Seluruh pustaka pendukung diinstal langsung ke dalam lingkungan komputasi Google Colab, meliputi Volatility 3, NetworkX, dan modul visualisasi.

📄 sel_1.py
python
# Pemasangan dependensi utama di Google Colab
!pip install -q volatility3 networkx jinja2 pandas

import sys
import os
import json
import re
import networkx as nx
from IPython.display import display, HTML

print("[INFO] Pustaka berhasil dipasang:")
print(f" - Python Version : {sys.version.split()[0]}")
print(f" - NetworkX       : {nx.__version__}")
print("[OK] Lingkungan komputasi Google Colab siap digunakan.")
Console Output
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.4/1.4 MB 16.1 MB/s eta 0:00:00
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 74.8/74.8 kB 2.7 MB/s eta 0:00:00
[?25h[INFO] Pustaka berhasil dipasang:
 - Python Version : 3.13.16
 - NetworkX       : 3.7
[OK] Lingkungan komputasi Google Colab siap digunakan.

2. Pemilihan Sumber Data Citra Memori (Rujukan Paper & Live Dump)

Notebook ini menyediakan 4 opsi pengujian:

  • Opsi 1 (CONTI_RANSOMWARE): Skenario serangan Conti Ransomware (Penyamaran svchost.exe PID 4812, C2 IP 185.220.101.45, dan VSS deletion).
  • Opsi 2 (LOCKBIT_RANSOMWARE): Skenario serangan LockBit 3.0 Ransomware (Process Hollowing pada spoolsv.exe PID 1824, lockbit.exe PID 3320, C2 IP 194.26.29.112, dan ekstensi .lockbit).
  • Opsi 3 (SAMSCLASS_MEMDUMP): Eksperimen live Volatility 3 mengunduh citra RAM fisik nyata Samsclass (memdump.mem ukuran ~17 MB terkompresi).
  • Opsi 4 (CUSTOM_FILE): Membaca berkas dump RAM Anda sendiri (.raw / .vmem / .dmp) jika tersedia.
📄 sel_2.py
python
# Konfigurasi Pemilihan Sumber Data
# Pilihan: 'CONTI_RANSOMWARE', 'LOCKBIT_RANSOMWARE', 'SAMSCLASS_MEMDUMP', atau 'CUSTOM_FILE'
SUMBER_DATA = 'CONTI_RANSOMWARE'

# Path jika memilih CUSTOM_FILE (misal diletakkan di Google Drive: '/content/drive/MyDrive/evidence.raw')
CUSTOM_IMAGE_PATH = '/content/evidence.raw'

print(f"[INFO] Mode Sumber Data Terpilih: {SUMBER_DATA}")
Console Output
[INFO] Mode Sumber Data Terpilih: CONTI_RANSOMWARE
📄 sel_3.py
python
# Pembuatan Folder Kerja dan Penyiapan Data
WORKSPACE_DIR = "/content/dfir_workspace"
os.makedirs(WORKSPACE_DIR, exist_ok=True)
os.makedirs(os.path.join(WORKSPACE_DIR, "output"), exist_ok=True)

artifacts_data = {
    "pslist": [],
    "malfind": [],
    "netscan": [],
    "handles": [],
    "cmdline": []
}

if SUMBER_DATA == 'CONTI_RANSOMWARE':
    print("[INFO] Mengompilasi artefak benchmark Conti Ransomware Incident...")
    # 1. pslist
    artifacts_data["pslist"] = [
        {"PID": 4, "PPID": 0, "ImageFileName": "System", "Offset": "0xfa8001c9b040", "Threads": 142, "Handles": 0, "CreateTime": "2026-10-07 08:00:01"},
        {"PID": 368, "PPID": 4, "ImageFileName": "smss.exe", "Offset": "0xfa8002a11040", "Threads": 2, "Handles": 28, "CreateTime": "2026-10-07 08:00:03"},
        {"PID": 492, "PPID": 368, "ImageFileName": "csrss.exe", "Offset": "0xfa8002a83040", "Threads": 11, "Handles": 450, "CreateTime": "2026-10-07 08:00:05"},
        {"PID": 568, "PPID": 368, "ImageFileName": "wininit.exe", "Offset": "0xfa8002b11040", "Threads": 3, "Handles": 115, "CreateTime": "2026-10-07 08:00:06"},
        {"PID": 672, "PPID": 568, "ImageFileName": "services.exe", "Offset": "0xfa8002ba5040", "Threads": 18, "Handles": 490, "CreateTime": "2026-10-07 08:00:07"},
        {"PID": 688, "PPID": 568, "ImageFileName": "lsass.exe", "Offset": "0xfa8002be9040", "Threads": 8, "Handles": 850, "CreateTime": "2026-10-07 08:00:07"},
        {"PID": 840, "PPID": 672, "ImageFileName": "svchost.exe", "Offset": "0xfa8002c91040", "Threads": 24, "Handles": 380, "CreateTime": "2026-10-07 08:00:10"},
        {"PID": 928, "PPID": 672, "ImageFileName": "svchost.exe", "Offset": "0xfa8002d12040", "Threads": 14, "Handles": 210, "CreateTime": "2026-10-07 08:00:12"},
        {"PID": 2412, "PPID": 568, "ImageFileName": "explorer.exe", "Offset": "0xfa8003110040", "Threads": 45, "Handles": 1420, "CreateTime": "2026-10-07 08:05:22"},
        # PROSES INFILTRASI ANOMALI
        {"PID": 4812, "PPID": 2412, "ImageFileName": "svchost.exe", "Offset": "0xfa8003b55040", "Threads": 6, "Handles": 125, "CreateTime": "2026-10-07 09:14:30"},
        {"PID": 5924, "PPID": 4812, "ImageFileName": "conti_locker.exe", "Offset": "0xfa8003f19040", "Threads": 32, "Handles": 980, "CreateTime": "2026-10-07 09:18:15"},
        {"PID": 6108, "PPID": 5924, "ImageFileName": "powershell.exe", "Offset": "0xfa8004101040", "Threads": 4, "Handles": 82, "CreateTime": "2026-10-07 09:18:40"}
    ]
    # 2. malfind (Injeksi VAD)
    artifacts_data["malfind"] = [
        {
            "PID": 4812,
            "Process": "svchost.exe",
            "Start_VPN": "0x7fff20000000",
            "Protection": "PAGE_EXECUTE_READWRITE",
            "CommitCharge": 4,
            "HexDump": "4d 5a 90 00 03 00 00 00 04 00 00 00 ff ff 00 00  MZ..............\ne8 00 00 00 00 5d 83 ed 0d 89 ea 52 68 00 00 00  .....].....Rh..."
        },
        {
            "PID": 5924,
            "Process": "conti_locker.exe",
            "Start_VPN": "0x000002050000",
            "Protection": "PAGE_EXECUTE_READWRITE",
            "CommitCharge": 16,
            "HexDump": "55 8b ec 83 ec 14 53 56 57 8b 7d 08 8b 75 0c 8b  U.....SVW.}..u..\n45 10 33 c9 85 f6 74 1a 8a 14 0e 88 14 07 41 3b  E.3...t.......A;"
        }
    ]
    # 3. netscan (Soket C2)
    artifacts_data["netscan"] = [
        {"Proto": "TCP", "LocalAddr": "192.168.1.105", "LocalPort": 49152, "ForeignAddr": "185.220.101.45", "ForeignPort": 4444, "State": "ESTABLISHED", "PID": 4812, "Owner": "svchost.exe"},
        {"Proto": "TCP", "LocalAddr": "192.168.1.105", "LocalPort": 135, "ForeignAddr": "0.0.0.0", "ForeignPort": 0, "State": "LISTENING", "PID": 840, "Owner": "svchost.exe"},
        {"Proto": "TCP", "LocalAddr": "192.168.1.105", "LocalPort": 445, "ForeignAddr": "0.0.0.0", "ForeignPort": 0, "State": "LISTENING", "PID": 4, "Owner": "System"},
        {"Proto": "TCP", "LocalAddr": "192.168.1.105", "LocalPort": 49210, "ForeignAddr": "91.240.118.172", "ForeignPort": 8080, "State": "SYN_SENT", "PID": 5924, "Owner": "conti_locker.exe"}
    ]
    # 4. handles (File, Mutex)
    artifacts_data["handles"] = [
        {"PID": 4812, "Type": "Mutant", "Name": "\\BaseNamedObjects\\msagent_beacon_mutex"},
        {"PID": 5924, "Type": "Mutant", "Name": "\\BaseNamedObjects\\CONTI_RANSOMWARE_MUTEX_0x44"},
        {"PID": 5924, "Type": "File", "Name": "C:\\Users\\Admin\\Documents\\skripsi_final_magister.docx.conti"},
        {"PID": 5924, "Type": "File", "Name": "C:\\Users\\Admin\\Documents\\data_keuangan_kampus.xlsx.conti"},
        {"PID": 5924, "Type": "File", "Name": "C:\\Users\\Admin\\Desktop\\readme.txt"},
        {"PID": 2412, "Type": "File", "Name": "C:\\Windows\\explorer.exe"},
        {"PID": 688, "Type": "Key", "Name": "\\REGISTRY\\MACHINE\\SAM\\SAM"}
    ]
    # 5. cmdline
    artifacts_data["cmdline"] = [
        {"PID": 4812, "Process": "svchost.exe", "Args": "C:\\Windows\\System32\\svchost.exe -k netsvcs -p -s BITS"},
        {"PID": 5924, "Process": "conti_locker.exe", "Args": "C:\\Users\\Admin\\AppData\\Local\\Temp\\conti_locker.exe -m net -size 10 -nomutex"},
        {"PID": 6108, "Process": "powershell.exe", "Args": "powershell.exe -w hidden -enc dmFkbWluIGRlbGV0ZSBzaGFkb3dzIC9hbGwgL3F1aWV0"}
    ]
    print(f"[OK] Artefak benchmark berhasil dimuat: {len(artifacts_data['pslist'])} proses.")

elif SUMBER_DATA in ['LOCKBIT_RANSOMWARE', 'BENCHMARK_LOCKBIT']:
    print("[INFO] Mengompilasi artefak benchmark LockBit 3.0 Ransomware (Keluarga 2 dari Paper volGPT - Elsevier 2024)...")
    # 1. pslist (12 proses: spoolsv.exe korban hollowing, powershell dropper, lockbit.exe, cmd.exe)
    artifacts_data["pslist"] = [
        {"PID": 4, "PPID": 0, "ImageFileName": "System", "Offset": "0xfa8001c9b040", "Threads": 138, "Handles": 0, "CreateTime": "2026-10-08 10:00:01"},
        {"PID": 368, "PPID": 4, "ImageFileName": "smss.exe", "Offset": "0xfa8002a11040", "Threads": 2, "Handles": 30, "CreateTime": "2026-10-08 10:00:02"},
        {"PID": 492, "PPID": 368, "ImageFileName": "csrss.exe", "Offset": "0xfa8002a83040", "Threads": 10, "Handles": 420, "CreateTime": "2026-10-08 10:00:03"},
        {"PID": 568, "PPID": 368, "ImageFileName": "wininit.exe", "Offset": "0xfa8002b11040", "Threads": 3, "Handles": 110, "CreateTime": "2026-10-08 10:00:04"},
        {"PID": 672, "PPID": 568, "ImageFileName": "services.exe", "Offset": "0xfa8002ba5040", "Threads": 16, "Handles": 480, "CreateTime": "2026-10-08 10:00:05"},
        {"PID": 688, "PPID": 568, "ImageFileName": "lsass.exe", "Offset": "0xfa8002be9040", "Threads": 7, "Handles": 810, "CreateTime": "2026-10-08 10:00:05"},
        {"PID": 840, "PPID": 672, "ImageFileName": "svchost.exe", "Offset": "0xfa8002c91040", "Threads": 20, "Handles": 350, "CreateTime": "2026-10-08 10:00:07"},
        {"PID": 2110, "PPID": 568, "ImageFileName": "explorer.exe", "Offset": "0xfa8003110040", "Threads": 42, "Handles": 1390, "CreateTime": "2026-10-08 10:05:00"},
        # PROSES INFILTRASI LOCKBIT 3.0
        {"PID": 1824, "PPID": 672, "ImageFileName": "spoolsv.exe", "Offset": "0xfa8003310040", "Threads": 12, "Handles": 290, "CreateTime": "2026-10-08 10:00:15"},
        {"PID": 2840, "PPID": 2110, "ImageFileName": "powershell.exe", "Offset": "0xfa8003710040", "Threads": 5, "Handles": 95, "CreateTime": "2026-10-08 10:12:10"},
        {"PID": 3320, "PPID": 2840, "ImageFileName": "lockbit.exe", "Offset": "0xfa8003990040", "Threads": 28, "Handles": 890, "CreateTime": "2026-10-08 10:13:00"},
        {"PID": 4012, "PPID": 3320, "ImageFileName": "cmd.exe", "Offset": "0xfa8003bb0040", "Threads": 2, "Handles": 45, "CreateTime": "2026-10-08 10:13:20"}
    ]
    # 2. malfind (Injeksi Process Hollowing pada spoolsv.exe dan biner lockbit.exe)
    artifacts_data["malfind"] = [
        {
            "PID": 1824,
            "Process": "spoolsv.exe",
            "Start_VPN": "0x000001800000",
            "Protection": "PAGE_EXECUTE_READWRITE",
            "CommitCharge": 8,
            "HexDump": "4d 5a 90 00 03 00 00 00 04 00 00 00 ff ff 00 00  MZ.............."
        },
        {
            "PID": 3320,
            "Process": "lockbit.exe",
            "Start_VPN": "0x000002100000",
            "Protection": "PAGE_EXECUTE_READWRITE",
            "CommitCharge": 16,
            "HexDump": "55 8b ec 83 ec 14 53 56 57 8b 7d 08 8b 75 0c 8b  U.....SVW.}..u.."
        }
    ]
    # 3. netscan (Koneksi C2 LockBit 3.0)
    artifacts_data["netscan"] = [
        {"Proto": "TCP", "LocalAddr": "192.168.1.110", "LocalPort": 49188, "ForeignAddr": "194.26.29.112", "ForeignPort": 8443, "State": "ESTABLISHED", "PID": 3320, "Owner": "lockbit.exe"},
        {"Proto": "TCP", "LocalAddr": "192.168.1.110", "LocalPort": 135, "ForeignAddr": "0.0.0.0", "ForeignPort": 0, "State": "LISTENING", "PID": 840, "Owner": "svchost.exe"}
    ]
    # 4. handles (File .lockbit, Mutex LockBit, dan Catatan Tebusan)
    artifacts_data["handles"] = [
        {"PID": 3320, "Type": "Mutant", "Name": "\\BaseNamedObjects\\Global\\{LOCKBIT_3_MUTEX_0x99}"},
        {"PID": 3320, "Type": "File", "Name": "C:\\Users\\Admin\\Documents\\laporan_keuangan_q3.xlsx.lockbit"},
        {"PID": 3320, "Type": "File", "Name": "C:\\Users\\Admin\\Desktop\\Restore-My-Files.txt"},
        {"PID": 2110, "Type": "File", "Name": "C:\\Windows\\explorer.exe"}
    ]
    # 5. cmdline
    artifacts_data["cmdline"] = [
        {"PID": 2840, "Process": "powershell.exe", "Args": "powershell.exe -ExecutionPolicy Bypass -w hidden -enc JABzACAAPQAgAE4AZQB3AC0ATwBiAGoAZQBjAHQA"},
        {"PID": 3320, "Process": "lockbit.exe", "Args": "C:\\Temp\\lockbit.exe -k -pass lockbit3_master_key"},
        {"PID": 4012, "Process": "cmd.exe", "Args": "cmd.exe /c vssadmin delete shadows /all /quiet"}
    ]
    print(f"[OK] Artefak LockBit 3.0 berhasil dimuat: {len(artifacts_data['pslist'])} proses.")

elif SUMBER_DATA in ['SAMSCLASS_MEMDUMP', 'DOWNLOAD_PUBLIC_SAMPLE']:
    print("[INFO] Mengunduh citra memori publik Samsclass Live RAM Dump (memdump.7z)...")
    sample_url = "https://samsclass.info/121/proj/memdump.7z"
    os.system("apt-get install -y -q p7zip-full > /dev/null 2>&1")
    os.system(f"wget -q -O /content/memdump.7z {sample_url}")
    os.system("7z x /content/memdump.7z -o/content/ -y > /dev/null 2>&1")
    target_mem = "/content/memdump.mem"

    def parse_vol3_output(raw_str):
        try:
            data = json.loads(raw_str)
            if isinstance(data, list) and len(data) > 0 and isinstance(data[0], dict):
                first = data[0]
                cols_key = "Columns" if "Columns" in first else ("columns" if "columns" in first else None)
                rows_key = "Rows" if "Rows" in first else ("rows" if "rows" in first else None)
                if cols_key and rows_key:
                    cols = [c.replace(" ", "_") for c in first[cols_key]]
                    res = []
                    for item in data:
                        for r in item.get(rows_key, []):
                            res.append(dict(zip(cols, r)))
                    return res
                return data
            elif isinstance(data, dict):
                cols_key = "Columns" if "Columns" in data else ("columns" if "columns" in data else None)
                rows_key = "Rows" if "Rows" in data else ("rows" if "rows" in data else None)
                if cols_key and rows_key:
                    cols = [c.replace(" ", "_") for c in data[cols_key]]
                    return [dict(zip(cols, r)) for r in data[rows_key]]
        except Exception:
            pass
        return []

    if os.path.exists(target_mem):
        mem_size_mb = os.path.getsize(target_mem) / (1024 * 1024)
        print(f"[OK] Citra memori publik berhasil diekstrak: {target_mem} ({mem_size_mb:.2f} MB)")
        print("[INFO] Menjalankan Volatility 3 live (dengan argumen '-r json' yang benar)...")
        import subprocess
        for plugin in ["pslist", "malfind", "netscan", "cmdline"]:
            print(f"       -> Mengekstrak windows.{plugin}...")
            try:
                # Argumen -r json WAJIB diletakkan SEBELUM nama plugin!
                res = subprocess.run(["vol", "-f", target_mem, "-r", "json", f"windows.{plugin}"],
                                     capture_output=True, text=True, timeout=180)
                parsed = parse_vol3_output(res.stdout) if res.stdout else []
                if parsed:
                    artifacts_data[plugin] = parsed
                    print(f"          [OK] windows.{plugin}: {len(parsed)} baris data dimuat.")
                else:
                    err_msg = res.stderr.strip().splitlines()[-1] if res.stderr.strip() else "Tidak ada data baris"
                    print(f"          [INFO] windows.{plugin}: {err_msg}")
            except Exception as e:
                print(f"          [WARN] Gagal mengekstrak windows.{plugin}: {e}")
    else:
        print("[ERROR] Berkas /content/memdump.mem tidak ditemukan.")

elif SUMBER_DATA == 'CUSTOM_FILE':
    print(f"[INFO] Memeriksa keberadaan berkas citra memori: {CUSTOM_IMAGE_PATH}")
    if os.path.exists(CUSTOM_IMAGE_PATH):
        print(f"[OK] Berkas ditemukan: {os.path.getsize(CUSTOM_IMAGE_PATH) / (1024*1024):.2f} MB")
        print(f"[INFO] Menjalankan Volatility 3 terhadap: {CUSTOM_IMAGE_PATH}...")
        import subprocess
        for plugin in ["pslist", "malfind", "netscan", "cmdline"]:
            try:
                res = subprocess.run(["vol", "-f", CUSTOM_IMAGE_PATH, "-r", "json", f"windows.{plugin}"],
                                     capture_output=True, text=True, timeout=180)
                parsed = parse_vol3_output(res.stdout) if res.stdout else []
                if parsed:
                    artifacts_data[plugin] = parsed
            except Exception as e:
                print(f"[WARN] Ekstraksi windows.{plugin} gagal: {e}")
    else:
        print(f"[WARN] Berkas {CUSTOM_IMAGE_PATH} tidak ditemukan. Silakan unggah terlebih dahulu.")
Console Output
[INFO] Mengompilasi artefak benchmark Conti Ransomware Incident...
[OK] Artefak benchmark berhasil dimuat: 12 proses.

3. Tahap 2: Konstruksi Heterogeneous In-Memory Threat Graph (NetworkX) #

Memodelkan entitas memori RAM sebagai graf terarah G=(V,E)\mathcal{G} = (\mathcal{V}, \mathcal{E}):

  • Simpul (V\mathcal{V}): Entitas heterogen meliputi Proses (PROCESS), Ruang Memori Virtual (MEMORY_VAD), Koneksi Soket Jaringan (SOCKET), dan Objek Sistem (HANDLE).
  • Sisi (E\mathcal{E}): Relasi operasional meliputi hierarki proses (PARENT_OF), injeksi kode (HAS_INJECTION), koneksi keluar (OPENS_SOCKET), dan pembukaan objek sistem (HOLDS_HANDLE).
📄 sel_4.py
python
# Modul Correlator: Membangun Struktur Graf Ancaman Heterogen
class MemoryThreatGraph:
    def __init__(self):
        self.graph = nx.DiGraph()
        self.process_map = {}

    def build_graph(self, artifacts):
        self.graph.clear()
        self.process_map.clear()

        # 1. Tambahkan node PROCESS
        for proc in artifacts.get("pslist", []):
            pid = int(proc.get("PID", 0))
            ppid = int(proc.get("PPID", 0))
            name = proc.get("ImageFileName", proc.get("Process", "unknown.exe"))
            create_time = str(proc.get("CreateTime", ""))

            proc_data = {
                "id": f"PROC_{pid}",
                "type": "PROCESS",
                "pid": pid,
                "ppid": ppid,
                "name": name,
                "create_time": create_time,
                "cmdline": "",
                "injections": [],
                "sockets": [],
                "handles": [],
                "threat_score": 0,
                "mitre_techniques": []
            }
            self.process_map[pid] = proc_data

            self.graph.add_node(
                f"PROC_{pid}",
                node_type="PROCESS",
                label=f"{name} ({pid})",
                pid=pid,
                ppid=ppid,
                name=name
            )

        # 2. Hubungkan relasi hierarki (PARENT_OF)
        for pid, proc in self.process_map.items():
            ppid = proc["ppid"]
            if ppid in self.process_map:
                self.graph.add_edge(f"PROC_{ppid}", f"PROC_{pid}", relation="PARENT_OF", label="spawns")

        # 3. Integrasikan CMDLINE
        for cmd in artifacts.get("cmdline", []):
            pid = int(cmd.get("PID", 0))
            args = str(cmd.get("Args") or cmd.get("CommandLine") or "")
            if pid in self.process_map:
                self.process_map[pid]["cmdline"] = args
                self.graph.nodes[f"PROC_{pid}"]["cmdline"] = args

        # 4. Integrasikan INJEKSI MEMORI (malfind)
        for idx, mal in enumerate(artifacts.get("malfind", [])):
            pid = int(mal.get("PID", 0))
            start_vpn = str(mal.get("Start_VPN") or mal.get("StartVPN") or "0x0")
            prot = str(mal.get("Protection") or "UNKNOWN")
            vad_id = f"VAD_{pid}_{idx}"
            vad_data = {"id": vad_id, "start_vpn": start_vpn, "protection": prot}

            self.graph.add_node(vad_id, node_type="MEMORY_VAD", label=f"VAD [{prot}]", protection=prot)
            if pid in self.process_map:
                self.process_map[pid]["injections"].append(vad_data)
                self.graph.add_edge(f"PROC_{pid}", vad_id, relation="HAS_INJECTION", label="injected")

        # 5. Integrasikan SOKET JARINGAN (netscan)
        for idx, net in enumerate(artifacts.get("netscan", [])):
            pid = int(net.get("PID", 0))
            for_ip = str(net.get("ForeignAddr") or net.get("ForeignIP") or "0.0.0.0")
            for_port = str(net.get("ForeignPort") or "0")
            sock_id = f"SOCK_{pid}_{idx}"
            sock_data = {"id": sock_id, "foreign_ip": for_ip, "foreign_port": for_port, "proto": net.get("Proto", "TCP")}

            self.graph.add_node(sock_id, node_type="SOCKET", label=f"Socket -> {for_ip}:{for_port}", foreign_ip=for_ip, foreign_port=for_port)
            if pid in self.process_map:
                self.process_map[pid]["sockets"].append(sock_data)
                self.graph.add_edge(f"PROC_{pid}", sock_id, relation="OPENS_SOCKET", label="connects")

        # 6. Integrasikan HANDLES
        for idx, hdl in enumerate(artifacts.get("handles", [])):
            pid = int(hdl.get("PID", 0))
            h_type = str(hdl.get("Type") or "")
            name = str(hdl.get("Name") or "")
            if not name or any(skip in name.lower() for skip in ["\\knowndlls\\", "\\device\\ksecdd"]):
                continue

            hdl_id = f"HDL_{pid}_{idx}"
            hdl_data = {"id": hdl_id, "type": h_type, "name": name}
            self.graph.add_node(hdl_id, node_type="HANDLE", label=f"{h_type}: {name[:25]}", name=name)
            if pid in self.process_map:
                self.process_map[pid]["handles"].append(hdl_data)
                self.graph.add_edge(f"PROC_{pid}", hdl_id, relation="HOLDS_HANDLE", label="holds")

        return self.graph

threat_graph = MemoryThreatGraph()
nx_g = threat_graph.build_graph(artifacts_data)
print(f"[OK] Heterogeneous Threat Graph terbangun: {nx_g.number_of_nodes()} Nodes, {nx_g.number_of_edges()} Edges.")
Console Output
[OK] Heterogeneous Threat Graph terbangun: 25 Nodes, 24 Edges.

4. Tahap 3: Mesin Inferensi Deterministik MITRE ATT&CK

Mengevaluasi simpul proses dan relasinya terhadap taksonomi formal MITRE ATT&CK Enterprise Matrix v14:

  • T1055.001: Process Injection (DLL Injection) melalui proteksi VAD PAGE_EXECUTE_READWRITE.
  • T1036: Masquerading (Abnormal Parent-Child Hierarchy) melalui pelanggaran silsilah kernel Windows.
  • T1071.001: Application Layer Protocol (Command and Control) melalui koneksi keluar ke IP publik asing.
  • T1486: Data Encrypted for Impact melalui deteksi mutex unik atau ekstensi ransomware.
  • T1490: Inhibit System Recovery melalui deteksi eksekusi vssadmin delete shadows.
📄 sel_5.py
python
# Modul MITRE ATT&CK Mapper (Sistem Pakar Deterministik)
class MitreAttackMapper:
    STANDARD_SYSTEM_PARENTS = {
        "smss.exe": ["System"],
        "csrss.exe": ["smss.exe"],
        "wininit.exe": ["smss.exe"],
        "services.exe": ["wininit.exe"],
        "lsass.exe": ["wininit.exe"],
        "svchost.exe": ["services.exe"],
        "explorer.exe": ["userinit.exe", "winlogon.exe"]
    }

    RANSOMWARE_EXTS = [".conti", ".lockbit", ".wnry", ".crypted", ".locked"]
    RANSOM_NOTES = ["readme.txt", "how_to_decrypt", "restore_files"]

    def evaluate_process(self, proc_data, process_map):
        techs = []
        name = str(proc_data.get("name") or "").lower()
        cmdline = str(proc_data.get("cmdline") or "").lower()
        injections = proc_data.get("injections", [])
        sockets = proc_data.get("sockets", [])
        handles = proc_data.get("handles", [])
        ppid = proc_data.get("ppid", 0)

        # 1. T1055.001: Process Injection (RWX)
        for vad in injections:
            prot = str(vad.get("protection") or "").upper()
            if "EXECUTE_READWRITE" in prot or "EXECUTE_WRITECOPY" in prot:
                techs.append({
                    "id": "T1055.001",
                    "tactic": "Defense Evasion, Privilege Escalation",
                    "name": "Dynamic-link Library Injection",
                    "evidence": f"Alokasi memori VAD {vad.get('start_vpn')} memiliki hak akses berbahaya: {prot}."
                })
                break

        # 2. T1036: Masquerading (Parent Anomaly)
        parent_proc = process_map.get(ppid, {})
        parent_name = str(parent_proc.get("name") or "").lower()
        if name in self.STANDARD_SYSTEM_PARENTS:
            expected = [p.lower() for p in self.STANDARD_SYSTEM_PARENTS[name]]
            if parent_name and parent_name not in expected:
                techs.append({
                    "id": "T1036",
                    "tactic": "Defense Evasion",
                    "name": "Masquerading (Abnormal Parent Hierarchy)",
                    "evidence": f"Proses '{name}' dilahirkan oleh {parent_name} (PID {ppid}), seharusnya oleh {expected}."
                })

        # 3. T1071.001: C2 Communication
        for sock in sockets:
            for_ip = str(sock.get("foreign_ip") or "")
            for_port = str(sock.get("foreign_port") or "0")
            if for_ip and not for_ip.startswith("127.") and not for_ip.startswith("0.0.0.0"):
                if for_port in ["4444", "8080", "8443", "1337", "9001", "80", "443"]:
                    techs.append({
                        "id": "T1071.001",
                        "tactic": "Command and Control",
                        "name": "Web Protocols: Command & Control",
                        "evidence": f"Koneksi aktif ke IP C2 luar: {for_ip}:{for_port}."
                    })
                    break

        # 4. T1486: Ransomware Handles
        for hdl in handles:
            h_name = str(hdl.get("name") or "").lower()
            if any(ext in h_name for ext in self.RANSOMWARE_EXTS) or any(note in h_name for note in self.RANSOM_NOTES):
                techs.append({
                    "id": "T1486",
                    "tactic": "Impact",
                    "name": "Data Encrypted for Impact (Ransomware)",
                    "evidence": f"Proses memegang file handle ransomware: '{hdl.get('name')}'."
                })
                break

        # 5. T1490: Inhibit System Recovery
        if "vssadmin" in cmdline and "delete" in cmdline and "shadows" in cmdline:
            techs.append({
                "id": "T1490",
                "tactic": "Impact",
                "name": "Inhibit System Recovery",
                "evidence": f"Perintah pemusnahan Shadow Copies Windows: '{cmdline}'."
            })

        # 6. T1059.001: Encoded Scripting
        if "powershell" in name or "powershell" in cmdline:
            if any(flg in cmdline for flg in ["-enc", "-w hidden", "-nop"]):
                techs.append({
                    "id": "T1059.001",
                    "tactic": "Execution",
                    "name": "Command and Scripting: PowerShell",
                    "evidence": f"Eksekusi PowerShell terselubung: '{cmdline}'."
                })

        # Deduplikasi
        unique_t = []
        seen = set()
        for t in techs:
            if t["id"] not in seen:
                seen.add(t["id"])
                unique_t.append(t)
        return unique_t

mitre_mapper = MitreAttackMapper()
print("[OK] Mesin Inferensi Deterministik MITRE ATT&CK siap.")
Console Output
[OK] Mesin Inferensi Deterministik MITRE ATT&CK siap.

5. Tahap 4: Formulasi Kuantitatif Composite Threat Score #

Skor ancaman komposit Sthreat(p)∈[0,100]S_{threat}(p) \in [0, 100] untuk setiap proses pp dirumuskan sebagai:

Sthreat(p)=min⁡(100,∑i=1kwi⋅Ii(p))S_{threat}(p) = \min\left(100, \sum_{i=1}^{k} w_i \cdot \mathbb{I}_i(p)\right)

Dengan bobot indikator empiris:

  • w1=35w_1 = 35: Injeksi Memori VAD dengan proteksi PAGE_EXECUTE_READWRITE (RWX).
  • w2=25w_2 = 25: Koneksi soket aktif ke alamat IP C2 eksternal.
  • w3=20w_3 = 20: Penyamaran hierarki proses / PPID anomali (Masquerading).
  • w4=20w_4 = 20: Handle berkas ekstensi ransomware / ransom note.
  • w5=20w_5 = 20: Perintah penghapusan salinan bayangan (vssadmin delete shadows).
  • w6=15w_6 = 15: Eksekusi skrip terselubung / Base64 Encoded PowerShell.
📄 sel_6.py
python
# Modul Threat Scorer: Kalkulator Skor Risiko Komposit
class ThreatScorer:
    WEIGHTS = {
        "rwx_injection": 35,
        "c2_socket": 25,
        "masquerading": 20,
        "ransom_handles": 20,
        "inhibit_recovery": 20,
        "suspicious_script": 15
    }

    def calculate_score(self, proc_data, mitre_techniques):
        score = 0
        indicators = []
        t_ids = [t["id"] for t in mitre_techniques]

        if "T1055.001" in t_ids:
            score += self.WEIGHTS["rwx_injection"]
            indicators.append("Injeksi VAD RWX (+35)")
        if "T1071.001" in t_ids:
            score += self.WEIGHTS["c2_socket"]
            indicators.append("Koneksi C2 Aktif (+25)")
        if "T1036" in t_ids:
            score += self.WEIGHTS["masquerading"]
            indicators.append("Masquerading / Anomali PPID (+20)")
        if "T1486" in t_ids:
            score += self.WEIGHTS["ransom_handles"]
            indicators.append("Handle File/Mutex Ransomware (+20)")
        if "T1490" in t_ids:
            score += self.WEIGHTS["inhibit_recovery"]
            indicators.append("Pemusnahan Backup Windows (+20)")
        if "T1059.001" in t_ids:
            score += self.WEIGHTS["suspicious_script"]
            indicators.append("Skrip PowerShell Terselubung (+15)")

        final_score = min(100, score)

        if final_score >= 75:
            severity = "CRITICAL"
            badge_color = "#DC2626"
        elif final_score >= 50:
            severity = "HIGH"
            badge_color = "#EA580C"
        elif final_score >= 25:
            severity = "MEDIUM"
            badge_color = "#EAB308"
        else:
            severity = "LOW"
            badge_color = "#10B981"

        return {"score": final_score, "severity": severity, "badge_color": badge_color, "indicators": indicators}

    def score_all(self, process_map, mapper):
        compromised = []
        stats = {"total": len(process_map), "critical": 0, "high": 0, "medium": 0, "low": 0}

        for pid, proc in process_map.items():
            techs = mapper.evaluate_process(proc, process_map)
            proc["mitre_techniques"] = techs
            res = self.calculate_score(proc, techs)
            proc["threat_score"] = res["score"]
            proc["severity"] = res["severity"]
            proc["badge_color"] = res["badge_color"]
            proc["indicators"] = res["indicators"]

            if res["severity"] == "CRITICAL":
                stats["critical"] += 1
                compromised.append(proc)
            elif res["severity"] == "HIGH":
                stats["high"] += 1
                compromised.append(proc)
            elif res["severity"] == "MEDIUM":
                stats["medium"] += 1
            else:
                stats["low"] += 1

        compromised.sort(key=lambda x: x["threat_score"], reverse=True)
        stats["compromised"] = len(compromised)
        stats["top_compromised"] = compromised
        return stats

scorer = ThreatScorer()
process_map = threat_graph.process_map
eval_results = scorer.score_all(process_map, mitre_mapper)

print("================ HASIL EVALUASI TRIASE FORENSIK ================")
print(f" Total Proses Diinspeksi : {eval_results['total']}")
print(f" Proses Terinfeksi/Kritis: {eval_results['compromised']}")
print(f" Rincian Tingkat Risiko  : {eval_results['critical']} CRITICAL | {eval_results['high']} HIGH | {eval_results['medium']} MEDIUM | {eval_results['low']} LOW")
print("----------------------------------------------------------------")
for p in eval_results["top_compromised"]:
    print(f"[ALERT] PID {p['pid']} - {p['name']} [SKOR: {p['threat_score']}/100 - {p['severity']}]")
    print(f"        Induk (PPID): {p['ppid']} | Waktu Buat: {p['create_time']}")
    for t in p["mitre_techniques"]:
        print(f"        -> [{t['id']}] {t['name']}: {t['evidence']}")
    if p.get("cmdline"):
        print(f"        -> CMDLINE: {p['cmdline']}")
Console Output
================ HASIL EVALUASI TRIASE FORENSIK ================
 Total Proses Diinspeksi : 12
 Proses Terinfeksi/Kritis: 2
 Rincian Tingkat Risiko  : 2 CRITICAL | 0 HIGH | 0 MEDIUM | 10 LOW
----------------------------------------------------------------
[ALERT] PID 4812 - svchost.exe [SKOR: 80/100 - CRITICAL]
        Induk (PPID): 2412 | Waktu Buat: 2026-10-07 09:14:30
        -> [T1055.001] Dynamic-link Library Injection: Alokasi memori VAD 0x7fff20000000 memiliki hak akses berbahaya: PAGE_EXECUTE_READWRITE.
        -> [T1036] Masquerading (Abnormal Parent Hierarchy): Proses 'svchost.exe' dilahirkan oleh explorer.exe (PID 2412), seharusnya oleh ['services.exe'].
        -> [T1071.001] Web Protocols: Command & Control: Koneksi aktif ke IP C2 luar: 185.220.101.45:4444.
        -> CMDLINE: C:\Windows\System32\svchost.exe -k netsvcs -p -s BITS
[ALERT] PID 5924 - conti_locker.exe [SKOR: 80/100 - CRITICAL]
        Induk (PPID): 4812 | Waktu Buat: 2026-10-07 09:18:15
        -> [T1055.001] Dynamic-link Library Injection: Alokasi memori VAD 0x000002050000 memiliki hak akses berbahaya: PAGE_EXECUTE_READWRITE.
        -> [T1071.001] Web Protocols: Command & Control: Koneksi aktif ke IP C2 luar: 91.240.118.172:8080.
        -> [T1486] Data Encrypted for Impact (Ransomware): Proses memegang file handle ransomware: 'C:\Users\Admin\Documents\skripsi_final_magister.docx.conti'.
        -> CMDLINE: C:\Users\Admin\AppData\Local\Temp\conti_locker.exe -m net -size 10 -nomutex

6. Tahap 5: Visualisasi Graf Rantai Serangan Interaktif (Vis.js Network) #

Graf di bawah ini dirender secara interaktif langsung di dalam Google Colab.

  • Simpul Merah: Proses Kritis Terkompromi.
  • Simpul Merah Muda: Wilayah Injeksi Memori VAD RWX.
  • Simpul Oranye: Koneksi Soket C2 Aktif.
  • Simpul Hijau: Berkas Ransomware / Mutex yang Terkunci.
  • Anda dapat melakukan Zoom in/out, Menggeser (Drag), dan Mengklik Simpul untuk inspeksi bukti mendalam.
📄 sel_7.py
python
# Modul Visualisasi Graf Interaktif Vis.js di Colab
def render_colab_dashboard(nx_graph, process_map, eval_results):
    nodes_data = []
    edges_data = []

    for n, data in nx_graph.nodes(data=True):
        n_type = data.get("node_type", "UNKNOWN")
        threat_score = 0
        if n_type == "PROCESS":
            pid = data.get("pid", 0)
            threat_score = process_map.get(pid, {}).get("threat_score", 0)

        # Penentuan visual node
        if n_type == "PROCESS":
            color = "#DC2626" if threat_score >= 75 else ("#EA580C" if threat_score >= 50 else "#3B82F6")
            shape = "box"
            size = 28 if threat_score >= 75 else 20
        elif n_type == "MEMORY_VAD":
            color = "#F43F5E"
            shape = "dot"
            size = 18
        elif n_type == "SOCKET":
            color = "#F59E0B"
            shape = "diamond"
            size = 18
        elif n_type == "HANDLE":
            color = "#10B981"
            shape = "triangle"
            size = 16
        else:
            color = "#64748B"
            shape = "ellipse"
            size = 14

        nodes_data.append({
            "id": n,
            "label": data.get("label", n),
            "group": n_type,
            "shape": shape,
            "size": size,
            "color": {"background": color, "border": "#FFFFFF", "highlight": {"background": "#FBBF24", "border": "#FFFFFF"}},
            "font": {"color": "#FFFFFF", "size": 12, "face": "monospace"}
        })

    for u, v, data in nx_graph.edges(data=True):
        rel = data.get("relation", "")
        edges_data.append({
            "from": u,
            "to": v,
            "label": data.get("label", ""),
            "arrows": "to",
            "color": {"color": "#EF4444" if "INJECTION" in rel or "SOCKET" in rel else "#64748B", "highlight": "#FBBF24"},
            "font": {"color": "#94A3B8", "size": 10, "align": "middle"},
            "smooth": {"type": "curvedCW", "roundness": 0.2}
        })

    nodes_json = json.dumps(nodes_data)
    edges_json = json.dumps(edges_data)

    html_content = f'''
    <div style="background-color: #0B0F19; color: #F8FAFC; padding: 20px; border-radius: 12px; font-family: sans-serif;">
        <div style="display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #334155; padding-bottom: 12px; margin-bottom: 16px;">
            <div>
                <h2 style="margin: 0; color: #FFFFFF;">Interactive In-Memory Threat Graph (Colab View)</h2>
                <p style="margin: 4px 0 0 0; color: #94A3B8; font-size: 13px;">Visualisasi Korelasi Relasi Proses, VAD RWX, Soket C2, dan File Mutex</p>
            </div>
            <div style="display: flex; gap: 12px;">
                <span style="background: #DC2626; padding: 6px 12px; border-radius: 6px; font-weight: bold; font-size: 12px;">CRITICAL: {eval_results['critical']}</span>
                <span style="background: #EA580C; padding: 6px 12px; border-radius: 6px; font-weight: bold; font-size: 12px;">HIGH: {eval_results['high']}</span>
                <span style="background: #3B82F6; padding: 6px 12px; border-radius: 6px; font-weight: bold; font-size: 12px;">TOTAL PROSES: {eval_results['total']}</span>
            </div>
        </div>

        <div id="vis_network" style="width: 100%; height: 500px; background-color: #151D2F; border: 1px solid #334155; border-radius: 8px;"></div>

        <div style="margin-top: 16px; font-size: 12px; color: #94A3B8; display: flex; gap: 20px;">
            <div><span style="display:inline-block; width:12px; height:12px; background:#DC2626; margin-right:6px;"></span>Kotak Merah: Proses Kritis</div>
            <div><span style="display:inline-block; width:12px; height:12px; background:#F43F5E; border-radius:50%; margin-right:6px;"></span>Lingkaran Pink: Injeksi VAD RWX</div>
            <div><span style="display:inline-block; width:12px; height:12px; background:#F59E0B; transform:rotate(45deg); margin-right:6px;"></span>Diamond Oranye: Soket C2</div>
            <div><span style="display:inline-block; width:12px; height:12px; background:#10B981; margin-right:6px;"></span>Segitiga Hijau: File/Mutex</div>
        </div>
    </div>

    <script type="text/javascript" src="https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"></script>
    <script type="text/javascript">
        var container = document.getElementById('vis_network');
        var data = {{
            nodes: new vis.DataSet({nodes_json}),
            edges: new vis.DataSet({edges_json})
        }};
        var options = {{
            physics: {{
                stabilization: true,
                barnesHut: {{
                    gravitationalConstant: -3500,
                    springLength: 90,
                    springConstant: 0.04
                }}
            }},
            interaction: {{
                hover: true,
                navigationButtons: true,
                keyboard: true
            }}
        }};
        var network = new vis.Network(container, data, options);
    </script>
    '''
    return HTML(html_content)

# Render Visual Graf Langsung di Sel Google Colab
render_colab_dashboard(nx_g, process_map, eval_results)
Live In-Memory Threat Graph (Vis.js Interactive Engine)

7. Rekomendasi Tindakan Tanggap Insiden (DFIR Action Checklist) #

Berdasarkan hasil pemetaan MITRE ATT&CK dan nilai Composite Threat Score, tindakan terarah (Containment & Eradication) dapat segera diambil:

  1. Isolasi Jaringan Segera: Putus antarmuka jaringan host untuk memutus sesi aktif C2 ke IP 185.220.101.45.
  2. Terminasi Pohon Proses Malicious: Matikan secara paksa (kill process tree) svchost.exe (PID 4812) beserta seluruh anak prosesnya (conti_locker.exe PID 5924 dan powershell.exe PID 6108).
  3. Pencabutan Kunci Enkripsi di Memori: Periksa dump memori VAD 0x000002050000 untuk mengekstrak artefak kunci enkripsi simetris sebelum sistem di-reboot.
  4. Indikator Kompromi (IoC): Daftarkan IP C2 dan hash injeksi ke dalam firewall dan SIEM internal instansi.
Topik:#Digital Forensics#Google Colab#Jupyter Notebook#Volatility 3#MITRE ATT&CK#NetworkX#S2 Notes