« Phase 11 README

Hitchhiker's Guide — Cedar Lattice Phase 11: RE & Vuln Discovery Range

Operation Cedar Lattice. This guide walks through the fictional engagement environment for Phase 11. Meridian Freight International is the target. FIN-LATTICE is the emulated actor. Everything in this document is synthetic: binary metadata, source code snippets, IP addresses, domain names, and personnel are fabricated for educational use. No real malware is analyzed here. No real infrastructure is involved.


The Cedar Lattice Range Setup

Isolated Analysis Environment

Phase 11 RE work requires an isolated malware analysis environment. The range configuration:

ComponentDescription
Analysis VMUbuntu 22.04 LTS, 8 GB RAM, no internet access
SnapshotFresh snapshot taken before each sample — revert after analysis
NetworkHost-only adapter only — no bridge to host LAN, no internet routing
Shared folderDisabled — samples transferred via encrypted archive to prevent accidental execution
ToolsPython 3.11, Ghidra 10.3, pefile, python-magic, Detect-It-Easy

Why no internet? Binary samples may contain live C2 beacons. If executed or even partially parsed by a misconfigured tool on a networked host, the sample may attempt to phone home. Isolation prevents attribution of the analysis environment to FIN-LATTICE's C2 infrastructure.

The FIN-LATTICE Sample Set

Three synthetic samples are documented in this guide. Each has a corresponding metadata dict you can feed directly to the Lab 01 triage engine.


Sample 1: mfi_updater_v3.exe — Primary Cedar Lattice Artifact

This is the artifact documented in the Phase 11 README. It was found on Meridian Freight's internal update server during a file server enumeration. Filename and path:

\\MFIFILESERV01\IT_Tools\Updates\mfi_updater_v3.exe

Synthetic Metadata Dict

MFI_UPDATER_V3 = {
    "file_type": "PE32+",
    "architecture": "x86_64",
    "sections": [
        {"name": ".text",  "entropy": 7.92, "size": 65536},
        {"name": ".rsrc",  "entropy": 5.10, "size": 16384},
    ],
    "imports": [
        {"dll": "kernel32.dll", "function": "LoadLibraryA"},
        {"dll": "kernel32.dll", "function": "GetProcAddress"},
    ],
    "exports": [],
    "strings": [
        "http://198.51.100.45/update",
        "cmd.exe /c whoami",
        "198.51.100.45",
    ],
    "pdb_path": None,
    "timestamp": 0,
    "imphash": None,
    "opsec_flags": ["timestamp_zeroed", "no_pdb", "minimal_imports"],
}

Running the Triage Engine

from solution import triage_report

report = triage_report(MFI_UPDATER_V3)

Expected Output and Interpretation

{
    "file_type": "PE32+",
    "architecture": "x86_64",
    "packer": "likely_packed",         # entropy > 7.0, import count <= 3
    "high_entropy_sections": [".text"], # 7.92 > 7.0 threshold
    "suspicious_strings": [
        {"string": "http://198.51.100.45/update", "category": "url",      "severity": "HIGH"},
        {"string": "cmd.exe /c whoami",           "category": "shell_command", "severity": "HIGH"},
        {"string": "198.51.100.45",               "category": "ip_address",   "severity": "HIGH"},
    ],
    "imphash": None,
    "pdb_path": None,
    "classification": "loader",        # packer is truthy -> loader rule fires
    "opsec_flags": ["timestamp_zeroed", "no_pdb", "minimal_imports"],
}

Interpretation for the Cedar Lattice report:

The .text section at 7.92 bits/byte is highly compressed or encrypted. Two imports only means the unpacking stub is all that is statically visible. The classification is "loader": packing is confirmed and the binary will reconstruct its real functionality at runtime.

The three suspicious strings in the unencrypted .rsrc section reveal operational intent: a C2 URL (198.51.100.45) and a reconnaissance command (whoami). The zeroed timestamp and missing PDB path are deliberate opsec measures consistent with professional threat actor tooling.

Detection rule derivation:

This finding translates directly to detection rules:

# YARA-style detection concept (not functional — illustrative only)
rule FIN_LATTICE_LOADER {
    meta:
        description = "High-entropy PE with minimal imports and C2 URL strings"
    condition:
        pe_section_entropy > 7.0
        and pe_import_count <= 3
        and any_string matches /http:\/\/198\.51\.100\.\d+\//
}

A SIEM detection rule might alert on: endpoint process creation for mfi_updater_v3.exe OR DNS resolution for 198.51.100.45 from any Meridian Freight endpoint.


Sample 2: mfi_reflective_dll.dll — In-Memory Loader

Found injected into svchost.exe by the endpoint EDR memory scanner. This sample was never written to disk as a standalone file. It was recovered via process memory dump.

Synthetic Metadata Dict

MFI_REFLECTIVE_DLL = {
    "file_type": "DLL",
    "architecture": "x86_64",
    "sections": [
        {"name": ".text",  "entropy": 6.45, "size": 32768},
        {"name": ".rdata", "entropy": 4.20, "size": 8192},
        {"name": ".data",  "entropy": 3.80, "size": 4096},
    ],
    "imports": [
        {"dll": "kernel32.dll", "function": "LoadLibraryA"},
        {"dll": "kernel32.dll", "function": "GetProcAddress"},
        {"dll": "kernel32.dll", "function": "VirtualAlloc"},
    ],
    "exports": ["ReflectiveLoader", "DllMain"],
    "strings": [
        "ReflectiveDLLInjection",
        "NtCreateThreadEx",
        "svchost",
    ],
    "pdb_path": None,
    "timestamp": 0,
    "imphash": None,
    "opsec_flags": ["timestamp_zeroed", "no_pdb", "in_memory_only"],
}

Expected Triage Output

{
    "file_type": "DLL",
    "architecture": "x86_64",
    "packer": False,                      # entropy 6.45 is not > 7.0 for any section
    "high_entropy_sections": [],          # no sections above threshold
    "suspicious_strings": [
        {"string": "NtCreateThreadEx",       "category": "high_entropy_str", "severity": "LOW"},
        {"string": "ReflectiveDLLInjection", "category": "high_entropy_str", "severity": "LOW"},
        {"string": "svchost",                ...},  # too short for high_entropy_str
    ],
    "imphash": None,
    "pdb_path": None,
    "classification": "loader",           # "ReflectiveLoader" in exports -> loader rule
    "opsec_flags": ["timestamp_zeroed", "no_pdb", "in_memory_only"],
}

Interpretation:

The ReflectiveLoader export is the defining signal. Reflective DLL injection is Stephen Fewer's technique (2008, widely published): the DLL contains a custom loader function exported as ReflectiveLoader that can map the DLL into memory without the Windows loader's involvement, enabling the DLL to run without ever touching the filesystem.

The classification "loader" is correct: the ReflectiveLoader export fires the loader rule before the injector rule is considered. (The injector rule requires both NtAllocateVirtualMemory AND a write primitive — the string "NtCreateThreadEx" in the string list does not count as an import.)

Detection:

Memory-resident DLLs with ReflectiveLoader exports are detectable via:

  • EDR memory scanning for reflective loader signatures
  • API call monitoring: VirtualAlloc of RWX memory followed by execution
  • Behavioral: unsigned DLL mapped without corresponding file on disk

Sample 3: mfi_inject.exe — Process Injector

Found as a scheduled task artifact: C:\Windows\System32\Tasks\MFIHealthCheck. The task called a binary at a suspicious path with system-level privileges.

Synthetic Metadata Dict

MFI_INJECTOR = {
    "file_type": "PE32+",
    "architecture": "x86_64",
    "sections": [
        {"name": ".text",  "entropy": 6.10, "size": 24576},
        {"name": ".data",  "entropy": 4.50, "size": 4096},
    ],
    "imports": [
        {"dll": "ntdll.dll",    "function": "NtAllocateVirtualMemory"},
        {"dll": "ntdll.dll",    "function": "NtWriteVirtualMemory"},
        {"dll": "ntdll.dll",    "function": "NtCreateThreadEx"},
        {"dll": "kernel32.dll", "function": "OpenProcess"},
        {"dll": "kernel32.dll", "function": "GetCurrentProcessId"},
    ],
    "exports": [],
    "strings": [
        "injecting into target",
        "OpenProcess failed",
        "NtAllocateVirtualMemory failed",
    ],
    "pdb_path": None,
    "timestamp": 0,
    "imphash": "3a5f9c2d11e4b7a8",
    "opsec_flags": ["timestamp_zeroed", "no_pdb"],
}

Expected Triage Output

{
    "file_type": "PE32+",
    "architecture": "x86_64",
    "packer": False,                 # entropy 6.10 is not > 7.0; 5 imports > 3
    "high_entropy_sections": [],
    "suspicious_strings": [
        # All three strings are >= 16 chars, no spaces (false: "injecting into target" has spaces)
        # "OpenProcess failed" — has spaces -> not high_entropy_str
        # "NtAllocateVirtualMemory failed" — has spaces -> not high_entropy_str
        # "injecting into target" — has spaces -> not high_entropy_str
    ],
    "imphash": "3a5f9c2d11e4b7a8",
    "pdb_path": None,
    "classification": "injector",   # NtAllocateVirtualMemory + NtWriteVirtualMemory
    "opsec_flags": ["timestamp_zeroed", "no_pdb"],
}

Interpretation:

The NtAllocateVirtualMemory + NtWriteVirtualMemory combination fires the injector classification immediately. These are NT native API calls that bypass the Win32 API layer — the standard hooks that EDRs and AV products instrument. Using the NT layer directly is a deliberate evasion technique. The presence of NtCreateThreadEx (also a native API) confirms the execution mechanism.

Detection:

  • EDR behavioral detection: OpenProcess on sensitive targets (lsass.exe, explorer.exe) followed by VirtualAllocEx / WriteProcessMemory or their native NT equivalents
  • Kernel callback monitoring: PsSetCreateProcessNotifyRoutine allows drivers to observe process handle creation; commercial EDRs use this
  • SIEM alert: scheduled task creation pointing to unsigned binary at non-standard path

Lab 02: Scanning Meridian Freight's Source Code

The red team accessed Meridian Freight's source repository via a misconfigured Gitea instance exposed on dev.meridianfreight.com without authentication. Three modules from the logistics backend are flagged for review.

Snippet 1: Shipment Tracking Query Module

SHIPMENT_SNIPPET = {
    "language": "python",
    "code": '''\
import sqlite3

def get_shipment_by_id(conn, shipment_id):
    query = "SELECT id, origin, dest, status FROM shipments WHERE id = " + shipment_id
    return conn.execute(query).fetchone()

def search_shipments(conn, carrier, status):
    q = f"SELECT * FROM shipments WHERE carrier = '{carrier}' AND status = '{status}'"
    return conn.execute(q).fetchall()
''',
    "context": "Meridian Freight logistics backend — shipment query module",
}

Running scan_snippet(SHIPMENT_SNIPPET) produces:

Line 4: sql_string_concat / CRITICAL / CWE-89 SQL Injection
Line 8: sql_string_concat / CRITICAL / CWE-89 SQL Injection

Remediation: Replace with parameterized queries: conn.execute("SELECT ... WHERE id = ?", (shipment_id,))

Detection / monitoring: Web Application Firewall rules for SQL metacharacters in the shipment_id parameter. Structured query logging to detect anomalously long or syntactically unusual query strings in the database audit log.

Snippet 2: Fleet Diagnostic Endpoint

FLEET_SNIPPET = {
    "language": "python",
    "code": '''\
import os
import subprocess

def ping_fleet_vehicle(vehicle_ip):
    # Verify vehicle is reachable
    result = os.system("ping -c 2 " + vehicle_ip)
    return result == 0

def restart_vehicle_agent(vehicle_ip, agent_name):
    cmd = f"ssh fleet-admin@{vehicle_ip} systemctl restart {agent_name}"
    subprocess.run(cmd, shell=True)
''',
    "context": "Meridian Freight fleet management — diagnostic endpoint",
}

Running scan_snippet(FLEET_SNIPPET) produces:

Line 5: os_system / HIGH / CWE-78 Command Injection
Line 10: subprocess_shell_true / HIGH / CWE-78 Command Injection

Remediation: Use list-form subprocess calls with explicitly controlled arguments. Validate vehicle_ip against an allow-list of registered fleet vehicle IPs before use.

Snippet 3: Document Storage Service

DOC_SNIPPET = {
    "language": "python",
    "code": '''\
import os

DOCS_ROOT = "/var/app/freight-docs"

def serve_document(request):
    doc_name = request.args.get("doc")
    filepath = os.path.join(DOCS_ROOT, doc_name)
    with open(filepath, "rb") as f:
        return f.read()

def get_driver_manifest(args):
    driver_id = args["driver_id"]
    manifest_path = os.path.join("/etc/fleet/manifests", driver_id)
    with open(manifest_path) as f:
        return f.read()
''',
    "context": "Meridian Freight document storage — file serving module",
}

Running scan_snippet(DOC_SNIPPET) produces:

Line 12: path_join_user_input / MEDIUM / CWE-22 Path Traversal

Note: Line 7 (os.path.join(DOCS_ROOT, doc_name)) also uses os.path.join with a variable (doc_name) but the scanner checks for explicit user-input keywords (request, input, param, args). Line 7 reads from request.args.get() but the join itself does not contain those keywords on the same line. Line 12 is flagged because args appears on the same line as os.path.join(. A human reviewer would flag line 7 as well — the scanner is a starting point, not a substitute for manual review.

Remediation: Apply os.path.realpath() and prefix validation after os.path.join().


Cedar Lattice Finding-to-Detection Mapping

Every RE finding in this phase connects to a concrete detection opportunity:

FindingRE SignalDetection Method
mfi_updater_v3.exe classified as loaderHigh entropy + minimal importsYARA rule on PE section entropy; EDR process creation alert
C2 URL 198.51.100.45/update in stringsSuspicious string extractionDNS/firewall block for IOC IP; SIEM alert on HTTP connection to IP
cmd.exe /c whoami stringShell command indicatorEndpoint behavior monitoring for whoami execution
mfi_reflective_dll.dll with ReflectiveLoaderExport table analysisEDR memory scan for reflective loader signature
mfi_inject.exe with NT injection importsImport table analysisEDR behavioral detection for cross-process memory write
SQL injection in shipment queryCRITICAL source code findingWAF rule; query parameterization in code fix
Command injection in fleet diagnosticHIGH source code findingInput validation; restrict fleet admin endpoint to internal IP range
Path traversal in document serviceMEDIUM source code findingrealpath() + prefix check in code fix; WAF path traversal rule

Phase 11 Lab Quick Reference

# Set up environment
cd phase-11-reverse-engineering-vuln-discovery

# Lab 01 — Binary Triage Engine
cd lab-01-binary-triage-engine
pip install -r requirements.txt

# Develop your solution
# Edit lab.py

# Test stubs (all should fail with NotImplementedError)
pytest -q

# Test reference solution (all 19 should pass)
LAB_MODULE=solution pytest -q

# Lab 02 — Source Code Vulnerability Scanner
cd ../lab-02-source-code-vuln-scanner
pip install -r requirements.txt

# Test reference solution (all 13 should pass)
LAB_MODULE=solution pytest -q

« Phase 11 README