Skip to content

ATT&CK Mapping & Emulation Planning

Adversary emulation is the disciplined, repeatable replication of known adversary tactics, techniques, and procedures (TTPs) against a live environment to measure how defenses actually behave — and ATT&CK mapping is its planning spine: translating a threat profile (which adversary are we pretending to be) into a concrete, ordered, safety-reviewed execution plan of techniques (T1059.001, T1003.001, …), each with its expected telemetry, its detection hypothesis, and its abort criteria. Emulation planning answers four questions before anything executes: who are we emulating (threat profile), what will we execute (technique list with parameters), what should happen when we do (telemetry and detection expectations), and what do we do when it goes wrong (safety and rollback). Unplanned red teaming produces stories; planned emulation produces measurements.

The difference between a pentest and an emulation is the difference between “we got in” and “we measured exactly where your controls stopped working”. Organizations spend heavily on controls whose coverage they have never actually verified against the adversary behaviors those controls claim to stop — emulation is the verification mechanism, and ATT&CK mapping is what makes it systematic rather than anecdotal: coverage becomes a matrix you can read (which tactics have real detections, which are empty), gaps become a backlog with owners, and exercises become repeatable measurements rather than one-off events. For the red side, planning is what makes emulation safe enough to run in production-adjacent labs: every technique carries pre-declared blast radius and abort criteria, which is what separates emulation from chaos. For the blue side, the plan is a gift: it arrives with the exact telemetry to watch for, which converts a red team exercise from “here is what we did, sorry about the noise” into a joint measurement session. And for the organization as a whole, emulation planning is the practical implementation of threat-informed defense — the strategy of prioritizing detection investment against the techniques your actual adversaries use, in the order they use them, rather than spreading budget uniformly across a 600-technique matrix nobody can fully cover.

MITRE ATT&CK organizes adversary behavior as tactics (the why — a goal like Credential Access) containing techniques (the how — T1003) and sub-techniques (T1003.001 — LSASS memory). An emulation plan binds that taxonomy to your environment in four documents:

EMULATION PLANNING CYCLE
[1] THREAT PROFILE [2] TECHNIQUE SELECTION
which adversary? map profile TTPs -> ATT&CK
- real group, or hybrid - keep what applies to env
- behaviors, tooling, (no macOS TTPs on a
targeting, cadence Windows estate)
- sourced from reports - order by kill-chain stage
| |
v v
[4] AFTER-ACTION <--------- [3] EXECUTION PLAN
- detections triggered per-technique card:
- gaps -> backlog - procedure (exact commands)
- coverage matrix update - expected telemetry (EIDs)
- retest schedule - detection hypothesis
- safety: blast radius,
abort criteria, cleanup

The threat profile is the plan’s anchor. Two honest options exist: emulate a real, documented group (their published TTP sequence becomes your script — this is what commercial emulation plans do for known APT toolchains) or build a hybrid profile from the behaviors most relevant to your sector. Real-group emulation has a failure mode worth naming up front: adversary tooling is often unavailable, legally messy, or itself malicious, so mature plans emulate the behavior with controlled implementations (your own tooling from the M-series, public atomic tests) rather than the literal malware. Technique selection filters the profile through your environment: an emulation of macOS persistence on a Windows-only estate wastes everyone’s time, and cloud techniques without cloud scope are theater. The execution plan is the deliverable that matters: one card per technique with the exact procedure (commands, parameters, timing), the expected telemetry (Sysmon EIDs, Windows event IDs, EDR detections you want to fire), a written detection hypothesis (“when X executes, EID 1 with parent Office should fire”), and safety columns: blast radius, abort criteria (what observation stops the exercise), cleanup procedure, and rollback. After-action closes the loop into metrics: which techniques triggered detections (covered), which executed silently (gap — with the telemetry that should have fired), producing the coverage matrix update and the detection backlog. The cycle is deliberately circular — an emulation plan that runs once is a pentest; one that runs quarterly with a maturing coverage matrix is a program.

Planning techniqueWhat it producesToolingNotes
Threat profile selectionEmulation anchor (group or hybrid)Threat reports, vendor plansBehavior emulation > literal malware
ATT&CK Navigator layeringVisual coverage/plan mapsNavigator (web app)Layers per exercise, color-coded
Technique cardsPer-TTP execution specsTemplate + this module’s toolCommands + telemetry + safety
Telemetry mappingExpected-EID table per techniqueATT&CK data sources fieldWindows Security + Sysmon EIDs
Atomic test selectionReady-made test proceduresAtomic Red Team [verify repo]Parameterize, don’t trust blindly
Control-gap matrixCoverage heat mapNavigator + detection inventoryThe leadership deliverable
Sigma mappingDetections to rule IDsSigma catalogLink cards to your deployed rules
Baseline telemetry captureNormal vs exercise diffPre-exercise capture windowWithout baseline, review is guesswork
Deconfliction & change freezeExercise windowOps ticketingNo unannounced infrastructure changes
After-action scoringCovered/gap per techniqueThis module’s workflowDrives the detection backlog

attackplan.py — an emulation-plan builder: takes a technique list (ATT&CK IDs with short notes) and generates a complete plan skeleton — per-technique cards with procedure placeholders, expected telemetry (from a built-in technique→EID mapping for the common Windows techniques), detection-hypothesis lines, safety columns — plus a Navigator-importable layer JSON and a plan-level summary. stdlib only.

#!/usr/bin/env python3
"""attackplan.py — emulation plan generator from ATT&CK technique list.
Usage:
python attackplan.py --tech techs.json --plan PLAN.md --layer layer.json
techs.json schema:
[{"id": "T1566.001", "note": "phishing attachment w/ maldoc"},
{"id": "T1059.001", "note": "powershell dropper"},
...]
stdlib only. EID map covers common Windows techniques; unknown
techniques get an explicit TODO row (never silently empty).
"""
import argparse, json, sys, datetime
TECH_DATA = {
"T1566.001": {"name": "Phishing: Spearphishing Attachment",
"eids": ["Sysmon 1 (Office opens attachment)",
"Windows 4688 (process creation)"],
"hypo": "Office child process alert fires",
"safety": "lab mailbox only; no live user targeting"},
"T1059.001": {"name": "Command and Scripting Interpreter: PowerShell",
"eids": ["Windows 4104 (script block)",
"Sysmon 1 (powershell.exe lineage)"],
"hypo": "Office->powershell parent chain alert",
"safety": "use benign payload string; no -enc with live C2"},
"T1053.005": {"name": "Scheduled Task",
"eids": ["Windows 4698 (task created)",
"Sysmon 1 (schtasks.exe)"],
"hypo": "4698 with suspicious action path",
"safety": "name tasks EMU-*; remove in cleanup"},
"T1003.001": {"name": "OS Credential Dumping: LSASS Memory",
"eids": ["Sysmon 10 (process access, lsass)",
"Windows 4688 (rundll32 comsvcs)"],
"hypo": "Sysmon 10 with GrantedAccess mask + lsass target",
"safety": "isolated VM only; NEVER on production DCs"},
"T1547.001": {"name": "Boot Autostart: Registry Run Keys",
"eids": ["Sysmon 12/13 (registry add/set)"],
"hypo": "run-key write alert from user-writable path",
"safety": "EMU-valued keys only; cleanup step mandatory"},
"T1021.001": {"name": "Remote Services: RDP",
"eids": ["Windows 4624 type 10", "Sysmon 3 (flow)"],
"hypo": "new RDP logon correlation",
"safety": "lab segment only; disable after test"},
"T1071.001": {"name": "Application Layer Protocol: Web (C2)",
"eids": ["Sysmon 3 (HTTP flow)", "proxy log"],
"hypo": "beacon periodicity hunt fires",
"safety": "point at lab listener, never external infra"},
}
def load_techs(path):
try:
items = json.load(open(path, encoding="utf-8"))
except (OSError, json.JSONDecodeError) as e:
sys.exit(f"cannot load {path}: {e}")
out = []
for it in items:
tid = (it.get("id") or "").strip()
if not tid:
continue
out.append({"id": tid,
"note": it.get("note", "")})
return out
def card(t):
"""Render one technique card."""
data = TECH_DATA.get(t["id"])
lines = [f"### {t['id']} — {t['note'] or '(no note)'}"]
if data:
lines.append(f"**Name:** {data['name']}")
lines.append("**Expected telemetry:**")
for e in data["eids"]:
lines.append(f"- {e}")
lines.append(f"**Detection hypothesis:** {data['hypo']}")
lines.append(f"**Safety:** {data['safety']}")
else:
lines.append("**Name:** TODO (unknown to plan generator)")
lines.append("- Expected telemetry: TODO — look up "
"data sources for this technique")
lines.append("- Detection hypothesis: TODO")
lines.append("- Safety: TODO — mandatory before execution")
lines += ["**Procedure:** (paste exact commands here)",
"**Abort criteria:** (what observation stops this)",
"**Cleanup:** (rollback steps)",
""]
return "\n".join(lines)
def navigator_layer(techs):
"""Build a minimal ATT&CK Navigator layer JSON."""
return {
"name": f"emulation-plan-{datetime.date.today()}",
"versions": {"attack": "15", "navigator": "5",
"layer": "4.5"}, # [verify current versions]
"domain": "enterprise-attack",
"description": "Generated by attackplan.py",
"filters": {"platforms": ["Windows"]},
"techniques": [
{"techniqueID": t["id"], "score": 100,
"comment": t["note"] or "planned"}
for t in techs
],
"gradient": {"colors": ["#ff6666", "#ffe766", "#8ec843"],
"minValue": 0, "maxValue": 100},
"legendItems": [{"label": "planned in this exercise",
"color": "#ff6666"}],
}
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--tech", required=True)
ap.add_argument("--plan", default="PLAN.md")
ap.add_argument("--layer", default="layer.json")
args = ap.parse_args()
techs = load_techs(args.tech)
if not techs:
sys.exit("no techniques loaded")
today = datetime.date.today().isoformat()
known = [t for t in techs if t["id"] in TECH_DATA]
unknown = [t for t in techs if t["id"] not in TECH_DATA]
doc = [
f"# Emulation Plan — {today}",
"",
"## Scope & rules of engagement",
"- environment: (lab / production-adjacent — decide)",
"- window: (time-boxed; change freeze in effect)",
"- abort conditions: (global stop criteria)",
"- contacts: (exercise lead, blue lead, ops on-call)",
"",
f"## Techniques: {len(techs)} "
f"({len(known)} mapped, {len(unknown)} need manual data)",
"",
]
for t in techs:
doc.append(card(t))
open(args.plan, "w").write("\n".join(doc))
json.dump(navigator_layer(techs),
open(args.layer, "w"), indent=2)
print(f"[+] plan: {args.plan} ({len(techs)} cards)")
print(f"[+] layer: {args.layer} (import into Navigator)")
if unknown:
print(f"[!] {len(unknown)} techniques need manual "
f"telemetry/safety data before execution: "
f"{', '.join(t['id'] for t in unknown)}")
if __name__ == "__main__":
main()
  • The TECH_DATA map (lines 12-47) encodes the planning knowledge that makes the generator useful: for each common Windows technique, the expected telemetry (real Sysmon/Windows IDs — Sysmon 10 for LSASS access, 4104 for script blocks, 4698 for task creation), a one-line detection hypothesis, and a safety line. Entries absent from the map produce explicit TODO rows (lines 62-68) — a plan generator that silently omits telemetry data manufactures false confidence, so unknown techniques are loud.
  • card() (lines 50-72) renders the per-technique execution card — the plan’s atomic unit: name, telemetry list, hypothesis, safety, plus the three human fields the generator refuses to invent (procedure, abort criteria, cleanup). The refusal is the design: those fields depend on your environment.
  • navigator_layer() (lines 75-94) emits a Navigator-compatible layer: every technique scored and commented, Windows platform filter, gradient legend. Version fields are marked [verify] in the comment because ATT&CK/Navigator versions move; the layer shape itself has been stable [verify current spec].
  • The plan header (lines 118-124) hardcodes the sections that make a plan legal to run: scope, ROE, window, global abort conditions, contacts. An exercise without those five lines is not an exercise — the generator prints them unfilled so nobody mistakes the skeleton for a plan.

The plan reaches the environment as a series of controlled, pre-announced events rather than an attack: the technique cards’ procedures execute inside the declared window, against the declared scope, with the blue team holding the same document and watching their own telemetry. Each card’s expected telemetry line is what reaches detection engineering directly — before execution, the blue lead confirms which of those telemetry sources actually exist in the estate (a technique whose expected EIDs aren’t even collected is a coverage finding before anything runs). During the window, every executed technique either fires its detection hypothesis (validated control — the measurement goal) or executes silently (gap — the more valuable outcome, honestly), and the after-action records both. The plan’s safety columns reach the target too, in a different sense: abort criteria are what keep an emulation from becoming an incident — a credential-dump card that aborts on any production-DC scope is the difference between a measurement and a breach. After the window, the coverage matrix update reaches leadership as the single picture they actually need: which adversary behaviors this environment detects today, which it doesn’t, and the trend line since the last exercise. The quarterly re-run of the same plan then reaches the backlog: techniques that silently passed last quarter and still pass this quarter are not bad luck, they are unowned gaps — and the plan is the artifact that makes that ownership question impossible to dodge.

  1. Profile — hybrid threat profile drafted from sector threat reports; behaviors prioritized
  2. Mapping — profile TTPs translated to ATT&CK IDs; environment filter applied (Windows estate)
  3. Planning — attackplan.py generates cards, Navigator layer, and ROE skeleton
  4. Preflight — telemetry readiness verified per card (EIDs flowing, EID 10 masks correct)
  5. Execution — T1059.001 card runs in lab segment; 4104 + Sysmon 1 fire as hypothesized
  6. Gap — T1003.001 card executes; Sysmon 10 configured but alert rule missing: silent pass recorded
  7. After-action — coverage matrix updated: 9 covered, 3 gaps; gap T1003.001 assigned to detection backlog
  8. Retest — same plan re-run next quarter; gap closed and validated: measurement loop closed

telemetrycheck.py — a pre-exercise telemetry readiness checker: verifies, before any technique executes, that the sensors each technique card depends on are actually running and forwarding — Sysmon service state and config hash, Windows event channels reachable, recent-event freshness per expected EID, and EDR agent presence. An exercise that runs without its telemetry is wasted effort, and this preflight is the five minutes that catches it.

  • Per-card mapping, not generic health. The check consumes the same technique→EID map shape as attackplan.py, so “is T1003.001 measurable right now” is answerable — a generic “Sysmon is running” is not.
  • Freshness over presence. A channel can exist while forwarding is broken; the check requires events newer than N minutes per expected EID, which is the property exercises actually depend on.
  • Config hash visibility. Sysmon’s config hash goes into the report — exercises have been silently invalidated by config changes between runs, and the hash makes drift undeniable.
  • Exit codes for automation. --strict mode exits non-zero when any required telemetry is missing, so the preflight can gate the exercise kickoff in CI or a runbook script.
#!/usr/bin/env python3
"""telemetrycheck.py — pre-exercise telemetry readiness preflight.
Usage (Windows, elevated):
python telemetrycheck.py --tech techs.json [--fresh 15] [--strict]
Checks per expected EID: channel exists + last event newer than
--fresh minutes. Sysmon: service state + config hash.
stdlib only (winreg, subprocess).
"""
import argparse, datetime, hashlib, json, re, subprocess, sys
import winreg
CHANNELS = {
"Sysmon": (r"Microsoft-Windows-Sysmon/Operational"),
"Security": ("Security"),
"PowerShell": (r"Microsoft-Windows-PowerShell/Operational"),
"WinRM": (r"Microsoft-Windows-WinRM/Operational"),
}
SYSMON_EID_PREFIX = "Sysmon"
def sysmon_state():
"""Service state + config hash via sc queryex + reg query."""
state = {"running": False, "config_sha16": None}
r = subprocess.run(["sc", "query", "Sysmon64"],
capture_output=True, text=True)
if "RUNNING" in r.stdout:
state["running"] = True
try:
k = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE,
r"SYSTEM\CurrentControlSet"
r"\Services\Sysmon64\Parameters")
cfg, _ = winreg.QueryValueEx(k, "ConfigFile")
raw = open(cfg, "rb").read()
state["config_sha16"] = hashlib.sha256(raw).hexdigest()[:16]
state["config_path"] = cfg
except OSError:
pass
return state
def last_event_age(channel, minutes):
"""True if channel has an event newer than `minutes`."""
r = subprocess.run(
["wevtutil", "qe", channel, "/c:1", "/rd:true", "/f:xml"],
capture_output=True, text=True)
if r.returncode != 0:
return False
m = re.search(r'SystemTime="([^"]+)"', r.stdout)
if not m:
return False
try:
ts = datetime.datetime.fromisoformat(
m.group(1).replace("Z", "+00:00"))
except ValueError:
return False
age = datetime.datetime.now(datetime.timezone.utc) - ts
return age <= datetime.timedelta(minutes=minutes)
def check_tech(tid, eid_lines, fresh, report):
"""Parse EID lines from a card and verify each source."""
ok = True
for line in eid_lines:
src = line.split()[0]
if src == SYSMON_EID_PREFIX:
chan = CHANNELS["Sysmon"]
elif src == "Windows":
chan = CHANNELS["Security"] if "4104" not in line \
else CHANNELS["PowerShell"]
else:
report.append(("?", line, "unknown source"))
continue
fresh_ok = last_event_age(chan, fresh)
report.append((src, line,
"fresh" if fresh_ok else "STALE/EMPTY"))
ok = ok and fresh_ok
return ok
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--tech", required=True)
ap.add_argument("--fresh", type=int, default=15)
ap.add_argument("--strict", action="store_true")
args = ap.parse_args()
techs = json.load(open(args.tech, encoding="utf-8"))
sm = sysmon_state()
print(f"[*] sysmon running: {sm['running']} "
f"config: {sm.get('config_path','-')} "
f"sha16={sm.get('config_sha16','-')}")
if not sm["running"]:
print("[!] Sysmon not running — most checks "
"below will fail")
overall = True
for t in techs:
tid = t.get("id")
eids = t.get("eids") or []
if not eids:
print(f"[?] {tid}: no telemetry mapping — "
f"fill card manually")
continue
rep = []
card_ok = check_tech(tid, eids, args.fresh, rep)
mark = "OK " if card_ok else "GAP"
print(f"[{mark}] {tid}")
for src, line, verdict in rep:
print(f" {verdict:<12} {line}")
overall = overall and card_ok
if args.strict and not overall:
sys.exit(2)
print("\n[+] preflight complete"
+ ("" if overall else " — gaps above"))
if __name__ == "__main__":
main()
  • sysmon_state() (lines 22-38) answers the two Sysmon questions that matter: is the service running (sc query) and which config is it running — the SHA-16 of the config file goes into the exercise record, making silent mid-exercise config drift provable after the fact.
  • last_event_age() (lines 41-56) shells to wevtutil for the channel’s newest event in XML form and parses the SystemTime attribute — the stdlib-adjacent choice that avoids heavyweight event-log dependencies. Freshness (not channel existence) is the property checked, because a channel can exist for months while forwarding silently broke last week; an unparseable or empty channel reads as stale, which is the fail-safe direction.
  • check_tech() (lines 55-71) parses the same eids list shape the plan cards carry (lines from TECH_DATA), mapping Sysmon N and Windows NNNN prefixes to channels and marking each fresh or stale — per-technique readiness, not generic health.
  • The --strict exit path (line 100) is the automation seam: a kickoff runbook calls the preflight first and aborts the exercise on exit code 2 — turning “did we check telemetry?” from a human memory task into a gate.

build the technique list and plan first (attackplan.py)

Section titled “build the technique list and plan first (attackplan.py)”

$ python3 attackplan.py —tech techs.json —plan PLAN.md —layer layer.json [+] plan: PLAN.md (6 cards) [+] layer: layer.json (import into Navigator)

preflight on the lab endpoint (Windows, elevated PowerShell)

Section titled “preflight on the lab endpoint (Windows, elevated PowerShell)”

$ python telemetrycheck.py —tech techs.json —fresh 15 [*] sysmon running: True config: C:\Windows\sysmoncfg.xml sha16=9f2ab1… [OK ] T1059.001 fresh Windows 4104 (script block) fresh Sysmon 1 (powershell.exe lineage) [GAP] T1003.001 STALE/EMPTY Sysmon 10 (process access, lsass)

fix: Sysmon 10 not flowing — verify config mask,

Section titled “fix: Sysmon 10 not flowing — verify config mask,”

restart Sysmon, re-run preflight until every card shows OK

Section titled “restart Sysmon, re-run preflight until every card shows OK”

$ python telemetrycheck.py —tech techs.json —strict [+] preflight complete

The preflight’s findings are themselves detection-program findings: a stale Sysmon 10 channel or a missing PowerShell operational channel is estate telemetry debt that exists whether or not an exercise is scheduled — running the preflight monthly (or fleet-wide at scale) produces a standing telemetry-coverage report worth more than any single exercise. Your own tooling hygiene: both tools here are read-only on the endpoint (registry reads, sc query, wevtutil) — safe on lab and production-adjacent hosts alike, and quiet in EDR telemetry. The workflow’s defender-side counterpart is the after-action: exercise detections that fired become validated Sigma/rule IDs referenced in future cards; techniques that passed silently become backlog entries with the exact telemetry that should have fired attached, which makes the gap tickets actionable rather than philosophical. Wazuh/SIEM mapping of the readiness report depends on your forwarding topology [verify]; Suricata has no layer here — preflight is host-sensor territory by design.

  1. Fleet mode: run the preflight remotely across the estate via WinRM/WMI and produce a per-host readiness matrix — the fleet version of the five-minute check.
  2. Config drift watch: store per-host Sysmon config hashes and alert on change between runs.
  3. EDR-agent check: query vendor services/registry to verify the EDR sensor state per host.
  4. Card-level scoring rollup: emit per-technique readiness percentages for the plan summary.
  5. Baseline window diff: capture event volumes per channel 24h pre-exercise and diff during, isolating exercise-generated noise from background.
SignalSourceIDNotes
Planned technique executionsExercise windowper-card hypothesesBlue holds same plan as red
4104 script blocks from emulationPowerShell4104Validate decoded-content alerting
Sysmon 10 on lsass (controlled)SysmonEID 10 + GrantedAccessHighest-value validation target
4698 scheduled task (EMU-*)Windows Security4698Task name convention = easy filter
Run-key writes (EMU-valued)SysmonEID 12/13Persistence detection validation
Lab beacon flowsSysmon / proxyEID 3Beacon periodicity hunt validation
Preflight findingstelemetrychecklocal reportTelemetry debt backlog
Coverage matrix deltasAfter-actioninternal metricLeadership measurement
Gap retest confirmationNext exerciseper-cardTrend line for the backlog
Wazuh exercise correlationHIDS/SIEM[verify rule IDs per deployment]Tag exercise events distinctly

Emulation planning itself has an adversary-shaped failure mode worth naming: the plan is a published prediction of defender-observable behavior, and real adversaries do not publish theirs. Three asymmetries matter. Order of operations: published ATT&CK sequences are averages; real operators reorder, skip, and re-enter tactics non-linearly, so plans that march the kill chain in order train defenders on a rhythm no real adversary keeps — counter: shuffle technique order across exercise runs and include at least one non-linear path per plan. Hygiene asymmetry: emulation tooling is typically cleaner than real adversary tooling (no messy legitimate-process noise, no operational accidents), so defenders tuned on emulation telemetry learn a neater world — counter: inject realistic noise (concurrent admin activity, legitimate tools that behave similarly) into exercise windows. Detection-friendliness: emulation runs with logging known-good and sensors confirmed (the preflight above), while real adversaries arrive into unmeasured environments and sometimes break sensors first — counter: at least once per program, run a “degraded telemetry” variant of the plan where one sensor is deliberately broken, measuring how the detection story degrades. The structural lesson for both sides: an emulation program that only ever confirms its own hypotheses becomes a exercise in self-congratulation; the plan’s value comes from the friction between the neat, published, pre-flighted world and the messy one real adversaries operate in — and deliberately engineering some of that friction into the exercise is what keeps the measurements honest.

⚠️ What caught me: our first ATT&CK-mapped exercise was a triumph — every card’s detection fired, the coverage matrix lit up green, and leadership applauded. Three weeks later a real intrusion used two of the “covered” techniques and nothing fired, because the exercise had run with test accounts whose group policy differed from real users, and the GPO-side logging those detections depended on simply didn’t apply to the compromised account. The matrix measured our lab, not our estate. Since then, every plan’s scope section answers “whose credentials, whose segment, whose GPO” explicitly, and any technique validated only in lab conditions gets marked as such — a green cell that lies is worse than an honest gap.

  1. Draft a hybrid threat profile for your (lab) estate from two public threat reports; extract 8-10 behaviors and map them to ATT&CK IDs by hand — then run attackplan.py and compare its cards against your manual reasoning; disagreements are where your mapping assumptions hide.
  2. Import the generated layer into ATT&CK Navigator and color-code three views: planned-this-exercise, covered-by-detections, untested — the three-layer coverage picture that drives backlog meetings.
  3. Run telemetrycheck.py --strict on your lab endpoint before executing any card; fix every GAP it reports and record what was actually broken (this is your estate’s real telemetry debt, discovered in one afternoon).
  4. Execute two cards end-to-end (T1059.001 and T1547.001) inside the declared window with the blue role watching live: confirm each expected EID arrives with the expected fields, then run the after-action scoring honestly — including the “silently passed” column.
  5. Deliberately break one sensor (disable Sysmon’s 4104 coverage via config) and re-run the affected card; document how the detection story degrades and what the after-action looks like when telemetry lies — the degraded-mode drill most programs skip.
  • Technical: Differentiate emulation from penetration testing operationally, and explain where ATT&CK mapping lives in each.
  • Technical: What does a technique card need beyond the ATT&CK ID to be executable — and which of those fields must a human fill rather than a generator?
  • Scenario: Your exercise’s coverage matrix is fully green, but a real incident three weeks later defeats a “covered” technique. Diagnose what the measurement got wrong.
  • Conceptual: Why does behavior emulation beat literal malware emulation for most programs, and when would you need the literal artifact?
  • Conceptual: How does threat-informed defense prioritize detection investment differently from framework-compliance-driven coverage?
  • Deep-dive: Design the degraded-telemetry variant of an exercise: which sensors do you break, in what order, and what does the measurement isolate?
  • Practical: You have one quarter and one engineer to stand up an emulation program. What are the first three artifacts you build, and what do you deliberately defer?

F12 (Cyber Kill Chain & MITRE ATT&CK) · F9 (Virtualization & Lab Setup) · F11 (Threat Modeling) · BT7 (Threat Hunting) · T2 (Initial Access Emulation)