The Hitchhiker's Guide to Running an Adversary-Emulation Plan on the Range
The operator's walkthrough for Phase 01. The WARMUP explains the concepts (the lifecycle, the three lenses, ATT&CK, the Pyramid of Pain, the CTI→plan workflow, purple teaming). This guide shows you how to build and run an emulation plan on your own authorized range: pick a named actor from CTI, select ATT&CK techniques, write procedures with success criteria and stop conditions, run Atomic-Red-Team-style tests against the OWNED range, observe the telemetry, score detection coverage, and run the purple-team replay. Then tear it down.
Authorization is the whole job. Everything here happens on the owned range from the README range diagram, under the Phase 00 Rules of Engagement, with stop conditions and a deconfliction contact named before any technique runs. If you cannot name the owner, scope, stop conditions, and deconfliction contact for a step, you do not run it. Nothing in this guide is a live target, a payload, or a weapon — the labs themselves reason over synthetic metadata only.
0. The loop you are about to run
pick actor (CTI) → select ATT&CK techniques → write procedures + success criteria + stop conditions
│ │
▼ ▼
build the plan (Lab 01) ───────────────────────────────► RUN on the OWNED range (Atomic-style)
│ │
▼ ▼
observe telemetry → score detection coverage → PURPLE-TEAM REPLAY (detect-tune-rerun-confirm)
│ │
▼ ▼
reconstruct the campaign (Lab 02) → evidence packet → TEARDOWN
This is one pass of threat-informed defense: emulate FIN-LATTICE, find the gaps in Meridian's detections, write the missing detections, confirm they fire, and leave them behind.
1. Stand up the authorized range
Use the isolated range from the README. The minimum for this phase:
| Range zone | Hosts | Role in this phase |
|---|---|---|
| operator | a Linux box you own | runs the emulation plan / Atomic-style tests |
| victim | one Windows host (+ optional DC), one Linux host | where procedures execute; owned, snapshot-first |
| detection | a SIEM (Wazuh/Elastic) with Sysmon + ETW + Zeek/Suricata | collects telemetry; where you score coverage and write detections |
Hard preconditions (Phase 00):
- The range is isolated — no bridge to home, corporate, or public networks.
- Snapshot every victim host before the run; you will revert after.
- No real credentials, customer data, or personal data in any fixture, capture, or screenshot.
- The deconfliction contact and stop conditions are written into the run sheet before step 1.
- Egress is deny-by-default; the only "C2" is your owned framework to an owned host.
Verify telemetry is actually flowing before you attack (you cannot score coverage on a sensor that is not collecting):
operator → run a benign marker (e.g., `whoami` in a new shell on the victim)
detection → confirm Sysmon EID 1 (process create) for that shell arrived in the SIEM
If the marker does not show up, fix collection first. A blind sensor produces a false "gap."
2. Pick the actor and pull the CTI
For Operation Cedar Lattice the actor is FIN-LATTICE, a financially-motivated intrusion set. Pull its profile the way you would in a real engagement:
- the actor's ATT&CK Group page (its documented techniques),
- a CTID Adversary Emulation Plan if one exists for a comparable group,
- vendor intel and M-Trends for sector relevance,
- map each documented procedure up to a technique id (WARMUP Chapter 4).
FIN-LATTICE's profile for this run:
T1566 Phishing (initial-access)
T1059 Command and Scripting Interp. (execution)
T1003 OS Credential Dumping (credential-access)
T1021 Remote Services (lateral-movement)
T1071 Application Layer Protocol C2 (command-and-control)
T1486 Data Encrypted for Impact (impact)
That flat list is not the plan — it is the input to Lab 01.
3. Build the plan (Lab 01)
Feed the profile to the builder. The builder orders the techniques along the lifecycle, attaches a procedure stub + the telemetry each emits + a detection idea + success criteria, and computes coverage and gaps.
cd lab-01-emulation-plan-builder
LAB_MODULE=solution python3 -c "
import solution as s
for step in s.build_plan(['T1486','T1566','T1021','T1059','T1071','T1003']):
print(step['step'], step['tactic'], step['technique'], '-', step['name'])
print('coverage:', s.coverage_summary(['T1566','T1059','T1003','T1021','T1071','T1486']))
print('gaps:', s.coverage_gaps(['T1566','T1059','T1003','T1021','T1071','T1486']))
"
Expected (deterministic, ordered along the lifecycle):
1 initial-access T1566 - Phishing
2 execution T1059 - Command and Scripting Interpreter
3 credential-access T1003 - OS Credential Dumping
4 lateral-movement T1021 - Remote Services
5 command-and-control T1071 - Application Layer Protocol
6 impact T1486 - Data Encrypted for Impact
coverage: {'initial-access': 1, 'execution': 1, 'credential-access': 1, 'lateral-movement': 1, 'command-and-control': 1, 'impact': 1}
gaps: ('reconnaissance', 'resource-development', 'persistence', 'privilege-escalation', 'defense-evasion', 'discovery', 'collection', 'exfiltration')
Read the gaps line as a finding, not noise. It says: an emulation of FIN-LATTICE alone never tests Meridian's discovery, persistence, or exfiltration detections. That is a blind spot in your assessment (distinct from a blind spot in the defense) — and naming it is the staff-level move (WARMUP Chapter 5).
4. Turn each step into a run sheet entry
For every plan step, before you run anything, write down:
| Field | Example (step 3, T1003) |
|---|---|
| Procedure | run the documented credential-access check against the owned host's marker store |
| Owned target | VICTIM-WIN-01 (snapshotted) |
| Telemetry expected | Sysmon EID 10 — a handle opened to lsass.exe with suspicious access |
| Detection idea | Credential Guard; LSASS PPL; alert on LSASS handle opens |
| Success criteria | procedure executes AND EID 10 observed; record detected / gap |
| Stop condition | one host only; read-only marker store; abort on any prod-adjacent signal; revert on completion |
| Deconfliction | named SOC contact + run window; ping before and after |
This is the run sheet. No step runs without all seven fields filled.
5. Run the procedures — Atomic-Red-Team style, on the OWNED range
You execute each technique as a small, scoped, ATT&CK-mapped test against your owned victim host — the Atomic Red Team model. The point is to emit the behavior the sensor sees, not to cause harm:
- T1566 Phishing — deliver a seeded benign pretext from owned infra to a seeded mailbox in the range. (No live recipients, ever.)
- T1059 Execution — run a benign loader-shaped command in a new shell on the victim.
- T1003 Credential Access — run the documented check against a marker credential store (synthetic), so EID 10 fires without touching real secrets.
- T1021 Lateral Movement — authenticate from the operator host to a second owned victim over an authorized admin protocol, producing a type-3 logon.
- T1071 C2 — beacon from your owned C2 framework to an owned host over its documented profile.
- T1486 Impact — model the impact stage with a benign marker (rename a canary file, delete a test shadow copy) — never real encryption, never real data.
After each, ping the deconfliction contact ("step N complete"). Snapshot/revert between destructive- shaped steps.
6. Observe the telemetry
For each step, go to the detection range and confirm the expected data source actually produced the event:
| Step | Expected event | Where to look |
|---|---|---|
| T1566 | mail-gateway message metadata; mail-client child process | gateway logs; Sysmon EID 1 |
| T1059 | process-create + command line | Sysmon EID 1; PowerShell 4104 |
| T1003 | handle to lsass.exe | Sysmon EID 10 |
| T1021 | logon type 3; SMB/RDP/WinRM flow | EID 4624; Zeek conn log |
| T1071 | beacon periodicity; TLS fingerprint | proxy/DNS logs; Zeek/Suricata; JA3/JA4 |
| T1486 | mass file-rename; VSS deletion | Sysmon EID 11/23; EID 524 |
Record one of three outcomes per step (WARMUP Chapter 5):
DETECTED data source present AND a rule fired
VISIBLE-ONLY data source present, NO rule fired → detection gap (costs a rule)
BLIND data source NOT present → visibility gap (costs a sensor)
7. Score detection coverage
Build the coverage scorecard from the per-step outcomes. The simplest honest artifact is the ATT&CK Navigator layer colored by outcome:
T1566 DETECTED (gateway flagged the pretext) green
T1059 VISIBLE-ONLY (EID 1 present, no analytic) yellow → detection gap
T1003 DETECTED (LSASS-handle rule fired) green
T1021 VISIBLE-ONLY (logon present, no lateral analytic) yellow → detection gap
T1071 BLIND (no egress/proxy logging on that VLAN) red → visibility gap
T1486 DETECTED (VSS-delete rule fired) green
This scorecard is the Phase 01 portfolio artifact (alongside the plan). It is evidence, not belief — because it came from a live run, not a guess (WARMUP Chapters 5, 11).
8. Reconstruct the campaign (Lab 02)
Feed the observed events — scattered and out of order, as the SOC really sees them — to the reconstructor to produce the campaign narrative, the furthest stage reached, the earliest break point, and the Diamond pivot.
cd ../lab-02-killchain-campaign-reconstructor
LAB_MODULE=solution python3 -c "
import solution as s
evs = [
s.Event('e5',500,'T1486',infrastructure='203.0.113.7',capability='LatticeCrypt'),
s.Event('e1',100,'T1566',infrastructure='mail.evil.test',capability='LatticeLoader'),
s.Event('e3',300,'T1021',capability='LatticeLoader'),
s.Event('e2',200,'T1059'),
s.Event('e4',400,'T1071',infrastructure='203.0.113.7'),
]
print('order:', [r['event_id'] for r in s.reconstruct(evs)])
print('furthest:', s.furthest_tactic(evs))
print('earliest break:', s.earliest_break(evs))
print('pivot on infra:', s.pivot(evs, on='infrastructure'))
print('pivot on capability:', s.pivot(evs, on='capability'))
"
Expected:
order: ['e1', 'e2', 'e3', 'e4', 'e5']
furthest: impact
earliest break: {'tactic': 'initial-access', 'technique': 'T1566', 'break_control': 'phishing-resistant MFA; SPF/DKIM/DMARC; user reporting'}
pivot on infra: (('e4', 'e5'),)
pivot on capability: (('e1', 'e3'),)
Read it as the report does: the actor reached impact; the cheapest place to have stopped it was
initial-access (phishing-resistant MFA — "defend left"); and the Diamond pivot links the C2 event
to the impact event (shared C2 203.0.113.7) and the phish to the lateral move (shared
LatticeLoader), so the SOC should have connected those alerts into one campaign.
9. The purple-team replay
Now collapse the gaps. Sit red and blue together and run the per-technique loop (WARMUP Chapter 11) for every VISIBLE-ONLY and BLIND outcome from §7:
for each gap technique:
1. red re-runs the Atomic-style test on the owned range
2. blue watches their tooling live
3. BLIND → deploy/configure the sensor, then go to (1)
VISIBLE-ONLY → write the detection rule now (Sigma-style)
4. red RE-RUNS the technique → confirm the rule fires (regression)
5. flip the Navigator cell yellow/red → green; record the rule id
Example for T1059 (visible-only → detection):
detection: process_creation where parent in (winword.exe, outlook.exe)
and child in (powershell.exe, cmd.exe, wscript.exe)
red re-runs T1059 from a doc-shaped parent → rule fires → cell goes green → KEEP this rule
The durable output is the set of new, regression-confirmed detections the client keeps — the difference between "we got domain admin" and "you now detect FIN-LATTICE's techniques."
10. Evidence packet and teardown
Evidence packet (the sanitized, public-safe portfolio artifact):
- the emulation plan (Lab 01 output): ordered steps, procedures, telemetry, detections, success criteria, stop conditions;
- the ATT&CK Navigator layer: coverage + the gap map (§3) and the post-replay colored scorecard (§7);
- the campaign reconstruction (Lab 02 output): order, furthest stage, earliest break, Diamond pivot;
- the new detections written in the purple replay (§9), with their regression results;
- a short narrative mapping the run to the Mandiant lifecycle for the executive briefing.
Hash every artifact at collection; record source, time, collector, timezone, and any transformation. Never include real targets, credentials, personal data, or any C2/payload artifact.
Teardown:
1. revert every victim host to its pre-run snapshot
2. tear down the owned C2 and phishing infra in the range
3. confirm deny-by-default egress is restored
4. ping the deconfliction contact: engagement window closed
5. archive the evidence packet to the isolated management network only
11. Expected observations (so you know it worked)
- The plan comes back ordered along the lifecycle and deterministic — same input, same plan.
- Every step ships a procedure, telemetry, a detection, and a success criterion (the pairing).
- The gap line correctly lists the tactics FIN-LATTICE never exercises (recon, persistence, privesc, defense-evasion, discovery, collection, exfiltration, resource-development).
- The campaign reconstructs to
e1..e5; furthest = impact; earliest break = initial-access / T1566; the Diamond pivot links the two C2/impact events and the two loader events. - Each technique you ran produced its expected data source in the SIEM (or you found and fixed the visibility gap first).
- After the purple replay, every VISIBLE-ONLY cell that you wrote a rule for flips to DETECTED on re-run — the regression proves the detection is real.
12. Common false claims (and the truthful version)
| False claim | Why it's wrong | Truthful version |
|---|---|---|
| "We emulated FIN-LATTICE." (ran a grab-bag of tests) | emulation is actor-driven, ordered, intel-mapped | "We ran the actor's documented TTPs in lifecycle order, mapped to ATT&CK." |
| "We have coverage of T1003 — it's green." (never ran it) | belief is not evidence | "On a live run the LSASS-handle rule fired; here is the SIEM event." |
| "No coverage of T1071." (sensor wasn't collecting) | that's a visibility gap, not a detection gap | "T1071 was BLIND — no egress logging on that VLAN; deploy the sensor, then re-test." |
| "Shared C2 proves it's the same actor." | pivoting is evidence, not proof | "These events pivot on shared infra 203.0.113.7; that links them; attribution needs more." |
| "We blocked the malware hash, so we're protected." | bottom of the Pyramid of Pain — trivial to change | "We added a behavioral detection for the technique; the actor can't evade it by recompiling." |
| "The purple session is done — we were all in the room." | the loop is the point, not the room | "For each gap we wrote a rule and re-ran the technique until it fired (regression-confirmed)." |
| "We reached impact, so the engagement succeeded." | reaching impact is table stakes | "We reached impact and identified the earliest break point and left tested detections." |
If you can run this loop end to end on your own range — pick the actor, build the plan, run it safely, score honest coverage, reconstruct the campaign, and walk away with regression-confirmed detections — you have done the core of what Operation Cedar Lattice asks of Phase 01, and the core of what a Mandiant red team consultant does on a real adversary-emulation engagement.