Running a Full Purple Team Exercise End to End
This is Chapter 4 of the Purple Team notebook, and it puts the whole thing together. Chapter 1 defined purple teaming as a transparent, collaborative loop; Chapter 2 built the offensive engine (Atomic Red Team and CALDERA); Chapter 3 built the defensive engine (detection validation, tuning, coverage). Each of those is a capability. This chapter is the event that uses all of them — a planned, scoped, time-boxed exercise that starts with an objective and ends with a prioritised, owned program of remediation work with retest dates on the calendar.
The discipline of the earlier chapters carries here in full. A purple exercise executes real adversary behaviour — credential dumping, lateral movement, exfiltration — and every technique in it requires explicit, written authorization against systems the organisation owns or is contractually permitted to test. The difference from a covert red team is transparency, not permission: everyone knows the exercise is running, but it is still real attacker tradecraft against real infrastructure, and running any of it outside an authorized window is indistinguishable from an intrusion.
Why This Matters
Section titled “Why This Matters”A purple team capability that never runs a structured exercise degenerates into ad-hoc “hey, can you test this rule?” requests — useful, but unmeasured and unaccountable. The structured exercise is what converts scattered testing into a repeatable instrument you can point at a question (“can we detect the actor most likely to target us?”) and get a defensible, trended answer.
The exercise is also where the two halves of the previous chapters actually meet in the same room, in real time. Emulation without a defender watching is just red teaming; detection engineering without live technique execution is theory. The purple exercise is the deliberate collision of the two, with a feedback loop measured in minutes — the red operator fires a technique, the blue analyst checks whether it alerted, and if it did not, they diagnose why and often fix it before the exercise ends. That same-session fix is the signature of a mature purple program and the reason the format exists.
Done well, the output is not a report that gets filed. It is a set of tuned detections deployed during the exercise, a ranked list of gaps each with a named owner and a retest date, and a set of numbers — coverage, MTTD, close rate — that you can compare against the last exercise to prove the program is improving. This chapter is about producing exactly that.
flowchart LR
A[Objective +<br/>threat intel] --> B[Plan:<br/>technique list, RoE]
B --> C[Prepare:<br/>environment, safety, roles]
C --> D[Execute:<br/>fire - observe - collaborate]
D --> E[Score:<br/>outcome, MTTD, robustness]
E --> F[Report:<br/>owned gaps + retest dates]
F --> G[Remediate +<br/>deploy detections]
G --> H[Retest]
H -.->|trend across exercises| A
style D fill:#553c9a,color:#fff
style F fill:#22543d,color:#fff
Part 1: The Exercise Operating Model
Section titled “Part 1: The Exercise Operating Model”A purple exercise is a small, structured operation with defined roles. Even when one person wears several hats (Part 12), the roles remain distinct because they represent distinct responsibilities, and blurring them is how exercises lose rigour.
1.1 Cells and roles
Section titled “1.1 Cells and roles”flowchart TD
ED[Exercise Director<br/>owns objective, scope, go/no-go, abort]
subgraph White["White Cell - control"]
ED
SCRIBE[Scribe<br/>timeline, evidence, decisions]
end
subgraph Red["Red Cell - offense"]
RL[Red Lead]
RO[Operators<br/>fire techniques]
end
subgraph Blue["Blue Cell - defense"]
BL[Blue Lead]
AN[Analysts / detection engineers]
end
ED --> RL
ED --> BL
RL <-->|transparent, real-time| BL
SCRIBE -.captures.-> Red
SCRIBE -.captures.-> Blue
style White fill:#2c5282,color:#fff
| Role | Owns | Key responsibility during the run |
|---|---|---|
| Exercise Director | Objective, scope, RoE, go/no-go, the abort call | Keeps the exercise on-objective; makes the stop decision if blast radius is exceeded |
| White Cell | Neutral control and deconfliction | Confirms each technique is in-scope before it fires; holds the deconfliction channel to real SOC |
| Scribe | The record | Timestamps every technique, captures evidence, logs every decision and collaboration point |
| Red Lead / Operators | Technique execution | Fire techniques per the plan, at agreed pace, capturing exact commands and output |
| Blue Lead / Analysts | Detection and response | Watch telemetry, record the outcome per technique, tune and re-test in real time |
The role that gets skipped and should never be is the Scribe. Without a dedicated record-keeper, the exercise generates findings that nobody can reconstruct afterward — “did T1055 alert? I think so?” is not a finding. The scribe’s timeline is the raw material of the after-action report, and capturing it in real time is far cheaper than reconstructing it from memory and chat logs later.
1.2 The transparency principle
Section titled “1.2 The transparency principle”The defining feature is the live, two-way channel between red and blue. This is not a red team that blue is trying to catch; it is two teams jointly characterising the defensive posture. Concretely, during the run:
- Red announces each technique before or as it fires (“firing T1003.001 via comsvcs, now”).
- Blue reports what they see, in real time (“EID 10 present, no alert — logged-only”).
- When there is a gap, both sides diagnose it together and, where possible, fix it in-session.
That openness is what makes the feedback loop minutes long instead of weeks. It also changes the emotional dynamic: there is no “gotcha,” no defender embarrassment, no red-team victory lap — the shared goal is more covered techniques at the end of the day than at the start.
Part 2: Choosing the Exercise Format
Section titled “Part 2: Choosing the Exercise Format”Not every purple exercise looks the same. Three formats sit on a spectrum from cheapest/least-realistic to most-expensive/most-realistic, and choosing the wrong one wastes the day.
| Format | What happens | Best for | Cost |
|---|---|---|---|
| Tabletop | Walk through an attack scenario verbally; “if the actor did X, would we see it?” | Testing process, roles, and decision-making; onboarding; when tooling is not ready | Low |
| Technique-driven | Fire an ordered list of individual ATT&CK techniques (Atomic-style), validate each | Building and measuring detection coverage breadth | Medium |
| Adversary-emulation | Emulate a specific named actor’s full chain end to end (CALDERA/CTID plan) | Testing whether detections hold up when techniques are chained under realistic timing | High |
The most common mistake is jumping to full adversary emulation before the technique-driven groundwork exists. If individual techniques are not yet reliably detected, chaining them adds nothing but noise — you cannot attribute a missed detection in a 40-technique chain without first knowing each technique’s baseline. Technique-driven exercises build the coverage; emulation exercises stress-test it. Do them in that order.
A mature program cycles through all three: tabletop to validate a new playbook or team, technique-driven to expand and measure coverage, emulation to validate that coverage against the actors that actually matter — then back to technique-driven to close the gaps emulation exposed.
Part 3: Scoping and Rules of Engagement
Section titled “Part 3: Scoping and Rules of Engagement”Scope is what separates a purple exercise from an incident. Before anything fires, the boundaries must be written, signed, and understood by everyone in every cell.
3.1 The Rules of Engagement template
Section titled “3.1 The Rules of Engagement template”PURPLE TEAM EXERCISE - RULES OF ENGAGEMENT
1. AUTHORIZATION Authorizing authority: <name, title, signature, date> Systems in scope: <explicit host list / CIDR / cloud account IDs> Systems explicitly OUT: <production DBs, safety systems, third-party assets> Authorization window: <start datetime> to <end datetime>, <timezone>
2. OBJECTIVE Primary question: e.g. "Can we detect APT29-style credential access and lateral movement within our corp domain?" Success criteria: <measurable, e.g. detection outcome recorded for each of the N planned techniques; >=X% score-3 coverage>
3. TECHNIQUES Planned technique list: <reference the technique plan, Part 4> Explicitly forbidden: e.g. destructive actions, real data exfiltration, DoS, anything touching customer PII, ransomware encryption even in simulation
4. SAFETY & BLAST RADIUS Environment: <lab / prod-with-limits> Snapshot/rollback: <required before start; who owns it> Data handling: synthetic data only; no real credentials harvested Blast-radius limits: <e.g. lateral movement max 2 hops; no domain-wide>
5. DECONFLICTION Real SOC informed?: <yes/no - see Part 3.3> Deconfliction contact: <name, phone, always-reachable during window> Exercise marker: <how activity is tagged, e.g. a header, a known source host, a filename convention>
6. ABORT Abort authority: Exercise Director AND either lead Abort triggers: real incident detected, blast radius exceeded, production impact, safety concern Abort procedure: <stop firing, announce on channel, snapshot state, begin cleanup - see Part 5.4>
7. COMMUNICATIONS Exercise channel: <dedicated, logged> Reporting: <who gets the after-action report, by when>
Signatures: Exercise Director ___ Red Lead ___ Blue Lead ___ System Owner ___The two fields most often left vague and most important are “Systems explicitly OUT” and “Abort triggers.” Naming what is out-of-scope is what prevents a curious operator from pivoting into a production database because it was reachable; naming abort triggers in advance is what lets anyone stop the exercise without a debate when something goes wrong.
3.2 Blast-radius discipline
Section titled “3.2 Blast-radius discipline”Purple techniques are real, and some have real consequences if run without limits. Encode the limits explicitly:
| Technique class | Uncontrolled risk | Blast-radius control |
|---|---|---|
| Credential dumping | Real credentials harvested and now in a dump file | Synthetic accounts only; secure-delete dumps; rotate any real creds touched |
| Lateral movement | Uncontrolled spread across the domain | Cap hops (e.g. 2); named target hosts only |
| Persistence | Artifacts left running after the exercise | Track every persistence artifact in the scribe log; verify removal in cleanup |
| Defense evasion (disable logging) | Blind spot left open post-exercise | Re-enable and verify before close; never leave logging off |
| Exfiltration | Real data leaves the environment | Synthetic marker files only; never real data, even encrypted |
3.3 To deconflict with the real SOC, or not
Section titled “3.3 To deconflict with the real SOC, or not”A genuine design decision with two valid answers:
- Inform the SOC (deconflicted): the SOC knows the exercise is running and can distinguish it from a real incident. Use this when you are testing detection content — you do not want a real IR mobilisation, and you want analysts free to collaborate.
- Do not inform the SOC (blind / “double-blind”): the SOC does not know, so you also test whether the human response process works. Use this when the SOC’s ability to detect-and-respond under realistic conditions is itself the thing under test — but keep the White Cell deconfliction contact reachable so a real escalation can be halted before it wastes an on-call night or triggers external notifications.
Most technique-driven purple exercises are deconflicted, because the objective is detection coverage and collaboration. Reserve blind runs for when response-process realism is the explicit goal, and even then, only the front-line SOC is blind — the White Cell always knows.
Part 4: Threat-Intelligence-Driven Planning
Section titled “Part 4: Threat-Intelligence-Driven Planning”The technique list is the spine of the exercise, and the best technique lists come from intelligence about who actually targets you, not from a generic top-10.
4.1 From actor to ordered plan
Section titled “4.1 From actor to ordered plan”flowchart LR
A[Who targets our sector?<br/>threat intel] --> B[Pick a representative actor<br/>e.g. APT29 / FIN7]
B --> C[Extract their TTPs<br/>ATT&CK profile + reporting]
C --> D[Filter to techniques<br/>relevant to our environment]
D --> E[Order by kill-chain phase]
E --> F[Annotate each: data source,<br/>expected detection, robustness target]
F --> G[The technique plan]
style G fill:#22543d,color:#fff
The filtering step matters: an actor’s full profile may include techniques irrelevant to your estate (a Linux-only technique when you are Windows-domain, a cloud technique when you are on-prem). Emulate what is applicable, and note the rest as out-of-scope-this-round rather than pretending you covered them.
4.2 A worked technique plan
Section titled “4.2 A worked technique plan”An exercise plan is a table, ordered by kill-chain phase, with the detection expectation stated before the run so the result can be scored against a prediction:
| # | Phase | Technique | Procedure | Data source | Expected outcome | Robustness target |
|---|---|---|---|---|---|---|
| 1 | Discovery | T1087.002 Account Discovery: Domain | net group /domain | 4688 cmdline | Logged-only (predict gap) | L2 |
| 2 | Discovery | T1018 Remote System Discovery | nltest /dclist | 4688 cmdline | Logged-only | L2 |
| 3 | Cred Access | T1003.001 LSASS dump | comsvcs MiniDump | Sysmon EID 10 | Alerted (Ch.3 rule) | L3 |
| 4 | Cred Access | T1558.003 Kerberoasting | Rubeus | Security 4769 | Alerted (Ch.3 rule) | L3 |
| 5 | Lateral | T1021.002 SMB/Admin Shares | psexec | 4688 + 5140 | Predict gap | L2 |
| 6 | Lateral | T1550.002 Pass-the-Hash | Mimikatz sekurlsa::pth | 4624 type 9 | Predict gap | L2 |
| 7 | Collection | T1560.001 Archive via utility | 7z a | 4688 cmdline | Logged-only | L1 |
| 8 | Exfil | T1048 Exfil over alt protocol | DNS tunnel (marker) | Sysmon EID 22 | Predict gap | L2 |
Stating the expected outcome before the run is a discipline worth insisting on: it converts each technique from “let’s see what happens” into a testable prediction, and a surprise (predicted alerted, got nothing) is often more valuable than a confirmed gap because it means a detection you believed in is broken.
Part 5: Environment and Safety
Section titled “Part 5: Environment and Safety”5.1 Snapshots first
Section titled “5.1 Snapshots first”Nothing fires until there is a known-good rollback point. Every target host and the domain controller are snapshotted, and the snapshot owner is named in the RoE. This is non-negotiable for two reasons: it lets you cleanly remove persistence and artifacts, and it lets you re-run the exercise from an identical baseline, which is what makes results comparable across runs.
# Example: snapshot the lab before the exercise (hypervisor-specific;# shown for a libvirt/KVM lab)for vm in dc01 win10-workstation win-server-app; do virsh snapshot-create-as "$vm" "purple-baseline-$(date +%Y%m%d)" \ --description "pre-exercise clean state" --atomicdonevirsh snapshot-list dc01 Name Creation Time State------------------------------------------------------------------ purple-baseline-20270513 2027-05-13 08:41:07 +0000 shutoff5.2 The exercise marker
Section titled “5.2 The exercise marker”So that exercise activity can be told apart from real activity (and cleaned up), tag it. A dedicated source host, a filename convention, a benign but distinctive string in command lines — any consistent marker that the scribe records and the blue cell can filter on:
Exercise marker conventions (recorded in RoE): - all offensive activity originates from 10.0.14.55 (red-op host) - dropped files named PURPLE-<technique>-<timestamp>.<ext> - synthetic accounts prefixed svc-purple-* - DNS exfil marker domain purple-exercise.lab.internal5.3 The abort protocol
Section titled “5.3 The abort protocol”Rehearsed, one-line, executable by anyone with abort authority:
stateDiagram-v2
[*] --> Running
Running --> AbortCalled: real incident / blast radius / prod impact / safety
AbortCalled --> StopFiring: red ceases immediately
StopFiring --> Announce: "EXERCISE ABORT" on channel
Announce --> Freeze: scribe snapshots current state + timeline
Freeze --> Deconflict: white cell checks with real SOC
Deconflict --> Cleanup: remove artifacts, restore, rotate creds
Cleanup --> [*]
The abort must be practised, not just documented. An abort that has never been executed is a hypothesis, and the moment you need it is the worst possible time to discover the rollback does not work.
5.4 Cleanup and close-out
Section titled “5.4 Cleanup and close-out”Every exercise ends with a verified return to clean state, tracked against the scribe’s artifact log:
| Cleanup item | Verification |
|---|---|
| Persistence artifacts removed | Re-query for each artifact logged during the run; expect none |
| Dropped files deleted | Marker-convention sweep returns nothing |
| Synthetic accounts disabled/removed | Directory query for svc-purple-* |
| Logging re-enabled | Confirm any evasion-disabled source is back and forwarding |
| Real credentials rotated | Any real account whose creds were exposed is rotated |
| Snapshots restored (lab) | Hosts reverted to baseline; or artifacts individually confirmed gone (prod) |
Part 6: The Live Run — Cadence and the Collaboration Loop
Section titled “Part 6: The Live Run — Cadence and the Collaboration Loop”With plan, RoE, roles, and environment ready, the exercise runs. The rhythm is a tight per-technique loop, repeated down the plan.
6.1 The per-technique loop
Section titled “6.1 The per-technique loop”sequenceDiagram
participant R as Red operator
participant W as White cell / scribe
participant B as Blue analyst
R->>W: "Firing T1003.001 comsvcs MiniDump, now"
W->>W: confirm in-scope, timestamp T0
R->>R: execute technique, capture exact cmd + output
R->>B: "Fired. Source 10.0.14.55, target lsass on win-server-app"
B->>B: check telemetry: logged? alerted? MTTD?
B->>W: "EID 10 present at T0+3s. No alert. LOGGED-ONLY."
W->>W: record outcome, MTTD, evidence link
Note over R,B: If gap: diagnose together. If fixable now, blue<br/>deploys/tunes rule, red RE-FIRES to validate.
B->>W: "Rule deployed. Re-fire?"
R->>R: re-fire same technique
B->>W: "ALERTED at T0'+4s. Now covered L3."
W->>W: record the in-session fix
The in-session fix — the dashed part of the loop — is the single most valuable thing that happens in a purple exercise. A logged-only result diagnosed and turned into a validated alert before the technique moves on is coverage bought in minutes. Not every gap is fixable in-session (a not-logged gap usually needs a data-source project), but every logged-only gap is a candidate, and working them live is what distinguishes a purple exercise from sequential red-then-blue.
6.2 What the scribe captures per technique
Section titled “6.2 What the scribe captures per technique”The record for each technique is fixed and complete, because it becomes a row in the scorecard and the report:
technique_id: T1003.001procedure: comsvcs.dll MiniDumptimestamp_fired: 2027-05-13T10:14:03Zsource_host: 10.0.14.55target: win-server-app / lsass.exeexact_command: rundll32 comsvcs.dll, MiniDump <pid> C:\temp\PURPLE-...dmp fulltelemetry_seen: Sysmon EID 10, GrantedAccess 0x1fffff, T+3soutcome: logged-only -> (in-session) alertedmttd_seconds: 4 # after in-session fixrobustness: L3detection_ref: rules/credential_access/T1003.001_lsass_access.ymlnotes: default rule missing; deployed Ch.3 rule during exerciseevidence: splunk-link, screenshot6.3 Pace
Section titled “6.3 Pace”Resist the urge to rush the plan. A purple exercise that fires 40 techniques with no time to diagnose gaps produces a long list and no fixes — which is a red team wearing a purple badge. Better to plan 12-15 techniques with room to stop, diagnose, and fix on the logged-only results. Depth of collaboration beats breadth of technique count; the coverage you build during the exercise matters more than the coverage you merely measure.
Part 7: Scoring the Exercise
Section titled “Part 7: Scoring the Exercise”A purple exercise without a scoring model produces anecdotes. The scoring model turns each technique’s result into comparable numbers.
7.1 The scoring dimensions
Section titled “7.1 The scoring dimensions”| Dimension | Values | Measures |
|---|---|---|
| Detection outcome | not-logged / logged-only / alerted / prevented | The coverage state (Chapter 3, Part 3) |
| MTTD | seconds/minutes | Speed of detection, where detection exists |
| Robustness | L1-L4 (Chapter 3, Part 7) | Durability of the detection |
| Response quality | none / manual / playbook / automated | What happened after the alert |
| In-session fixed? | yes/no | Whether the gap was closed during the run |
Scoring on all five, rather than a single pass/fail, is what makes the result actionable: “logged-only, L-n/a, no response” and “alerted, L1, manual response” are both “not great” on a binary scale but need completely different fixes.
7.2 A worked scorecard
Section titled “7.2 A worked scorecard”The plan from Part 4.2, after the run:
| # | Technique | Outcome (start → end) | MTTD | Robustness | Response | In-session fix |
|---|---|---|---|---|---|---|
| 1 | T1087.002 Account Discovery | logged-only → alerted | 6s | L2 | manual | Yes |
| 2 | T1018 Remote System Discovery | logged-only → alerted | 5s | L2 | manual | Yes |
| 3 | T1003.001 LSASS dump | logged-only → alerted | 4s | L3 | manual | Yes |
| 4 | T1558.003 Kerberoasting | alerted | 47s | L3 | playbook | — |
| 5 | T1021.002 SMB lateral | not logged | — | — | none | No (needs 5140 onboarding) |
| 6 | T1550.002 Pass-the-Hash | logged-only | — | L2 | none | No (rule drafted, needs tuning) |
| 7 | T1560.001 Archive | logged-only → alerted | 8s | L1 | manual | Yes |
| 8 | T1048 DNS exfil | not logged | — | — | none | No (needs Sysmon EID 22) |
7.3 Reading the scorecard
Section titled “7.3 Reading the scorecard”Turn the table into the three numbers that go on the summary slide:
#!/usr/bin/env python3"""score_exercise.py - summarise a purple exercise scorecard."""results = [ ("T1087.002","alerted",6,"L2",True), ("T1018","alerted",5,"L2",True), ("T1003.001","alerted",4,"L3",True), ("T1558.003","alerted",47,"L3",False), ("T1021.002","not-logged",None,None,False), ("T1550.002","logged-only",None,"L2",False), ("T1560.001","alerted",8,"L1",True), ("T1048","not-logged",None,None,False),]n = len(results)alerted = [r for r in results if r[1] == "alerted"]logged_only = [r for r in results if r[1] == "logged-only"]not_logged = [r for r in results if r[1] == "not-logged"]robust = [r for r in alerted if r[3] in ("L3","L4")]fixed = [r for r in results if r[4]]mttds = [r[2] for r in alerted if r[2] is not None]
print(f"Techniques tested: {n}")print(f"Alerted (any robustness): {len(alerted)}/{n} ({100*len(alerted)//n}%)")print(f"Covered (alerted, L3+): {len(robust)}/{n} ({100*len(robust)//n}%)")print(f"Logged-only (cheap gaps): {len(logged_only)}")print(f"Not logged (visibility): {len(not_logged)}")print(f"Closed in-session: {len(fixed)}")print(f"Median MTTD (alerted): {sorted(mttds)[len(mttds)//2]}s")Techniques tested: 8Alerted (any robustness): 5/8 (62%)Covered (alerted, L3+): 2/8 (25%)Logged-only (cheap gaps): 1Not logged (visibility): 2Closed in-session: 5Two honest headlines fall out: the exercise raised alerted coverage from 1/8 (only Kerberoasting was pre-covered) to 5/8 during the run, and yet only 25% is covered at high robustness — a candid picture that a raw “62% detected” would have flattered. The five in-session fixes are the exercise’s immediate ROI; the three remaining gaps (one logged-only needing tuning, two not-logged needing telemetry projects) are the backlog. The gap between “alerted” and “covered at L3+” is the number that keeps a program honest, and it is exactly the technique-vs-procedure and robustness distinction from Chapter 3 showing up at the program level.
Part 8: Live Execution Walk-Through
Section titled “Part 8: Live Execution Walk-Through”To make the loop concrete, here is a chained sequence — discovery into credential access into lateral movement — as it actually plays out across all three cells. Everything runs in the authorized, snapshotted lab from Chapter 2, from the red-op host 10.0.14.55.
8.1 Discovery (T1087.002 / T1018)
Section titled “8.1 Discovery (T1087.002 / T1018)”Red fires:
C:\> net group "Domain Admins" /domainC:\> nltest /dclist:lab.internalGroup name Domain AdminsMembers-------------------------------------------------------------------------------Administrator svc-backup svc-sqlThe command completed successfully.
Get list of DCs in domain 'lab.internal' from '\\DC01' dc01.lab.internal [PDC] [DS] Site: Default-First-Site-NameThe command completed successfully.Blue observes (Splunk, filtering on the exercise source host):
`sysmon` OR (`wineventlog` EventCode=4688) host_ip=10.0.14.55| search CommandLine="*net group*" OR CommandLine="*nltest*"| table _time Image CommandLine_time Image CommandLine2027-05-13 10:02:11 C:\Windows\...\net.exe net group "Domain Admins" /domain2027-05-13 10:02:44 C:\Windows\...\nltest.exe nltest /dclist:lab.internalCollaboration dialogue (scribe log):
10:02 RED: fired T1087.002 (net group) + T1018 (nltest)10:02 BLUE: both in 4688 with cmdline. No alert. LOGGED-ONLY x2.10:04 BLUE: deploying discovery rule (net/nltest/whoami recon burst from single host)10:07 RED: re-firing both10:07 BLUE: ALERTED, MTTD 6s / 5s. Now L2 (matches tool cmdline, evadable by LOLBins). Flagged for a follow-up L3 behavioural: recon-burst-by-count regardless of tool.That last line is the discipline from Chapter 3’s robustness ladder in action: the in-session fix is a real L2 detection, and the team records that it is only L2 and what the L3 version would be, rather than declaring victory.
8.2 Credential access (T1003.001)
Section titled “8.2 Credential access (T1003.001)”Red fires (comsvcs, deliberately avoiding procdump to test robustness):
C:\> for /f "tokens=2" %i in ('tasklist ^| findstr lsass') do set PID=%iC:\> rundll32.exe C:\Windows\System32\comsvcs.dll, MiniDump %PID% C:\temp\PURPLE-lsass.dmp full (no output; C:\temp\PURPLE-lsass.dmp written, ~45 MB)Blue observes:
`sysmon` EventCode=10 TargetImage="*lsass.exe" host="win-server-app"| table _time SourceImage GrantedAccess CallTrace_time SourceImage GrantedAccess CallTrace2027-05-13 10:14:03 C:\Windows\...\rundll32.exe 0x1fffff ...comsvcs.dll+...Collaboration:
10:14 RED: fired T1003.001 via comsvcs (NOT procdump - testing robustness)10:14 BLUE: EID 10 present, SourceImage=rundll32. No alert. LOGGED-ONLY.10:16 BLUE: deploying Ch.3 rule (TargetImage=lsass + GrantedAccess mask, source-agnostic -> catches comsvcs too)10:18 RED: re-fire10:18 BLUE: ALERTED, MTTD 4s, L3 (technique-invariant, source-agnostic).10:19 WHITE: note - recommend RunAsPPL as prevention (Ch.3 Part 10). Backlog item.8.3 Lateral movement (T1021.002) — the gap that cannot be closed in-session
Section titled “8.3 Lateral movement (T1021.002) — the gap that cannot be closed in-session”Red fires:
C:\> psexec.exe \\win-server-app -u lab\svc-purple -p <synthetic> cmd.exe /c whoamilab\svc-purpleBlue observes — and finds nothing:
`wineventlog` EventCode=5140 host="win-server-app" -> 0 results`sysmon` EventCode=3 host_ip=10.0.14.55 DestinationPort=445 -> present, but no share-access detailCollaboration:
10:31 RED: fired T1021.002 (psexec to win-server-app)10:31 BLUE: network connection to 445 seen, but NO 5140 (file share access) events. Object Access auditing for file shares is not enabled on that host.10:33 WHITE: this is NOT-LOGGED, not logged-only. Cannot fix in-session. BACKLOG: enable "Audit Detailed File Share" (5145) + File Share (5140), owner = platform team, retest date = +2 weeks.This is the honest counterpoint to the in-session fixes: some gaps are visibility projects, not rule projects (Chapter 3, Part 10). The exercise’s job here is not to fix it live — it is to diagnose it precisely (not-logged, specific audit policy missing, named owner, retest date) so the after-action report carries a fix, not just a finding.
Part 9: Running It With Limited People and Tooling
Section titled “Part 9: Running It With Limited People and Tooling”Full white/red/blue cells are a luxury. Most teams run purple with two or three people and a modest SIEM. The exercise still works if you preserve the roles even when one person wears several hats.
9.1 The two-person purple exercise
Section titled “9.1 The two-person purple exercise”| Full model | Two-person compression |
|---|---|
| Exercise Director + White Cell + Scribe | Person A also directs and scribes (use a shared doc as the live timeline) |
| Red Lead + Operators | Person A fires techniques |
| Blue Lead + Analysts | Person B watches telemetry and tunes |
The one role you must not collapse into the others is the record. With two people, the timeline lives in a shared document that both edit in real time — every technique gets a row as it fires. It is tempting to “reconstruct it later”; later never comes, and the exercise’s value evaporates with the memory of it.
9.2 Minimal tooling stack
Section titled “9.2 Minimal tooling stack”You can run a complete, valuable purple exercise with entirely free tools:
Emulation: Invoke-AtomicRedTeam (per-technique) + a manual command listTelemetry: Sysmon (sysmon-modular config) + Windows Security auditingSIEM: a free-tier Splunk, Elastic, or even Windows Event Forwarding + queriesAuthoring: Sigma + sigma-cliRecord: a shared spreadsheet / markdown doc (the scribe timeline)Tracking: the scorecard script (Part 7.3) + an ATT&CK Navigator layerThe absence of a commercial breach-and-attack-simulation platform is not a blocker. Atomic Red Team plus Sysmon plus a query interface is enough to run the entire loop in Part 6, and many mature programs never use anything more.
9.3 Time-boxing for small teams
Section titled “9.3 Time-boxing for small teams”A two-person team cannot sustain a full-day, 40-technique exercise. Run short, frequent, focused sessions instead: a two-hour exercise covering 5-6 techniques in one ATT&CK tactic (say, Credential Access), fully diagnosed and fixed, every fortnight. Frequency beats scale for a small team — six focused two-hour sessions build more real coverage than one exhausting all-day sprint that measures a lot and fixes nothing.
Part 10: The After-Action Report
Section titled “Part 10: The After-Action Report”The report is the deliverable that converts a day of testing into a program of work. A findings list with no owners and no dates is a document that gets filed; a report structured around owned, dated remediation is one that changes posture.
10.1 Report structure
Section titled “10.1 Report structure”PURPLE TEAM EXERCISE - AFTER-ACTION REPORT
1. EXECUTIVE SUMMARY (half a page, for leadership) - Objective and the question answered - Headline numbers: techniques tested, alerted %, covered-at-L3+ %, in-session fixes, MTTD median - The single most important gap and its business relevance - Trend vs previous exercise (Part 11)
2. SCOPE & METHOD - RoE summary, format, actor emulated, dates, participants
3. RESULTS (the scorecard, Part 7.2) - Per-technique table: outcome, MTTD, robustness, response, fixed? - ATT&CK Navigator coverage layer (before/after)
4. FINDINGS & REMEDIATION (the heart of the report) For each gap: - Technique + procedure + evidence - Gap type (not-logged / logged-only / low-robustness / no-response) - Recommended fix (onboard source / write rule / tune / prevent) - OWNER (named person/team) <- mandatory - PRIORITY (from risk, not ease) - RETEST DATE <- mandatory
5. WHAT WORKED - Detections that fired well; keep and regression-test them
6. APPENDICES - Full scribe timeline, exact commands, raw evidence links10.2 The remediation table is the point
Section titled “10.2 The remediation table is the point”Every gap becomes a row that a program manager can track:
| Finding | Gap type | Fix | Owner | Priority | Retest |
|---|---|---|---|---|---|
| T1021.002 SMB lateral not detected | not-logged | Enable 5140/5145 file-share auditing on servers | platform-team | High | +2 wk |
| T1048 DNS exfil not detected | not-logged | Onboard Sysmon EID 22 (DNS query) | soc-eng | High | +3 wk |
| T1550.002 PtH logged-only | logged-only | Tune drafted 4624-type-9 rule to acceptable precision | detection-eng | Med | +1 wk |
| T1560.001 Archive detection is L1 | low-robustness | Add behavioural archive-of-sensitive-paths rule | detection-eng | Low | +4 wk |
| T1003.001 no prevention | prevention gap | Deploy RunAsPPL fleet-wide | endpoint-team | Med | +4 wk |
Two mandatory fields make or break the report: OWNER and RETEST DATE. A finding with no owner is nobody’s job; a fix with no retest date is never confirmed to actually work. The retest is not optional politeness — Chapter 3’s whole argument is that an unvalidated detection is a hypothesis, so a remediation is not “done” until a retest fires the technique and confirms the fix.
10.3 Closing the loop with retests
Section titled “10.3 Closing the loop with retests”Retests are mini-exercises: on the retest date, re-fire exactly the technique that exposed the gap, against the same target, and record the new outcome. Only a technique that moves from its gap state to alerted at the target robustness is closed. This is where the snapshot discipline (Part 5.1) pays off — an identical baseline makes the retest a clean before/after.
Part 11: Metrics and Trending Across Exercises
Section titled “Part 11: Metrics and Trending Across Exercises”A single exercise is a snapshot. The program’s value shows in the trend across exercises, and a small set of stable metrics makes that trend legible.
| Metric | Per-exercise | Trend meaning |
|---|---|---|
| Alerted coverage % | alerted / tested | Breadth of detection; should rise |
| Covered-at-L3+ % | robust / tested | Real coverage; the honest number |
| Median MTTD | across alerted techniques | Detection speed; should fall |
| In-session fix rate | fixed / gaps found | Team’s live detection-engineering capability |
| Remediation close rate | retested-closed / findings | Whether findings actually get fixed |
| Regression count | previously-covered techniques that broke | Rot; should stay near zero |
The two most diagnostic trend metrics are covered-at-L3+ % and remediation close rate. The first resists the flattery of “we detected it” by demanding robust, procedure-aware coverage (Chapter 3). The second measures the thing that actually determines whether the program improves posture: a program that finds 20 gaps per exercise and closes 3 is running in place; one that finds 8 and closes 8 is compounding. Close rate, not find rate, is the number that predicts whether next year’s exercise scores better.
flowchart LR
E1[Exercise 1<br/>covered 15%] --> E2[Exercise 2<br/>covered 25%]
E2 --> E3[Exercise 3<br/>covered 38%]
R1[retests close gaps] -.-> E2
R2[retests close gaps] -.-> E3
REG[regression testing<br/>keeps prior gains] -.-> E3
style E3 fill:#22543d,color:#fff
Part 12: Continuous and Automated Purple Teaming
Section titled “Part 12: Continuous and Automated Purple Teaming”The periodic exercise is the foundation; the maturity endpoint is making the loop continuous. Two complementary directions:
- Scheduled automated emulation. Tools like CALDERA (Chapter 2) and automated Atomic runners can fire a technique set on a schedule — nightly or weekly — and report which detections still fire. This is regression testing at the program level (Chapter 3, Part 1): it catches the log-source migration or EDR upgrade that silently broke twelve rules, days after it happens instead of at the next quarterly exercise.
- Detection-as-code integration. When a new detection is merged (Chapter 3, Part 8), a CI job fires the corresponding technique in a lab and confirms the rule alerts before it reaches production. The purple loop becomes part of the deployment pipeline rather than a calendar event.
flowchart TD
subgraph Periodic["Periodic (quarterly)"]
EX[Full purple exercise<br/>new actor, new techniques]
end
subgraph Continuous["Continuous (nightly/weekly)"]
AUTO[Automated emulation of<br/>the covered technique set]
AUTO --> CHK{Still alerting?}
CHK -->|no| REG[Regression ticket<br/>auto-filed]
CHK -->|yes| OK[Coverage confirmed]
end
subgraph Pipeline["On every detection merge"]
CI[CI fires technique<br/>-> confirms alert]
end
EX -->|adds techniques to| AUTO
REG -->|feeds| EX
style EX fill:#553c9a,color:#fff
style AUTO fill:#2c5282,color:#fff
The important boundary: automation regression-tests known coverage; it does not discover new gaps. Finding what you cannot yet detect still needs the creativity of a human-run exercise against a fresh actor profile. The mature program runs both — continuous automation to hold the line, periodic human exercises to advance it.
A note on where this notebook has been going: continuous purple teaming is the operational expression of everything across these four chapters. Chapter 1’s collaborative loop, Chapter 2’s emulation engines, Chapter 3’s validated detections, and this chapter’s structured exercise all converge on a single steady-state capability — an organisation that continuously proves, in evidence rather than belief, which adversary behaviours it can see and stop.
Part 13: Common Pitfalls and Myths
Section titled “Part 13: Common Pitfalls and Myths”13.1 Pitfalls
Section titled “13.1 Pitfalls”| # | Pitfall | Consequence | Fix |
|---|---|---|---|
| 1 | No scribe / no real-time record | Findings that cannot be reconstructed | Dedicated record; timeline written as it happens (Part 1.1) |
| 2 | Jumping to full emulation before technique coverage exists | Missed detections in a chain, unattributable | Technique-driven first, emulation to stress-test (Part 2) |
| 3 | Vague scope (“the corp domain”) | Operator pivots into something out of bounds | Explicit in/out host lists in the RoE (Part 3.1) |
| 4 | No abort triggers defined in advance | Debate while a real problem grows | Named triggers + rehearsed abort (Part 5.3) |
| 5 | Firing 40 techniques with no time to fix | A long list, zero coverage gained | 12-15 techniques with room to diagnose (Part 6.3) |
| 6 | No snapshot before start | Cannot cleanly remove artifacts or re-run | Snapshot everything first (Part 5.1) |
| 7 | Leaving logging disabled after evasion tests | A blind spot left open post-exercise | Cleanup checklist verifies re-enablement (Part 5.4) |
| 8 | Reporting “62% detected” without robustness | Overstated posture | Report covered-at-L3+ separately (Part 7.3) |
| 9 | Findings with no owner or retest date | Nothing gets fixed or confirmed | Owner + retest mandatory in the report (Part 10.2) |
| 10 | Treating the report as the deliverable | Filed and forgotten | The deliverable is closed retests, not a PDF |
| 11 | Measuring find rate, not close rate | Program runs in place | Trend close rate across exercises (Part 11) |
| 12 | Automation mistaken for the whole program | New gaps never discovered | Automation holds the line; humans advance it (Part 12) |
| 13 | Prevention validated but not its alert | Blind block, no campaign signal | Confirm prevented techniques also alert (Ch.3 Part 3) |
| 14 | Blind run with no White Cell contact | Real IR mobilised, external notifications fire | Front-line SOC blind, White Cell always reachable (Part 3.3) |
13.2 Myths
Section titled “13.2 Myths”“Purple teaming is just a red team that shares its report.” The defining feature is the live, two-way, minute-scale loop and the in-session fixes (Part 6.1). A red team that hands over a report at the end is not a purple exercise; the collaboration during execution is the point.
“You need a commercial breach-and-attack-simulation platform.” You need Atomic Red Team, Sysmon, a query interface, and Sigma — all free (Part 9.2). The platform is a convenience, not a prerequisite.
“More techniques per exercise is better.” Depth of collaboration beats breadth of count. Twelve techniques fully diagnosed and half of them fixed in-session beats forty measured and none fixed (Part 6.3).
“A high detection percentage means we’re covered.” Only if it is robust, procedure-aware coverage. The gap between “alerted” and “covered at L3+” is where the honesty lives (Part 7.3).
“The exercise is over when the report is written.” The exercise is over when the retests close. An unretested remediation is an unvalidated detection — a hypothesis (Part 10.3).
“We ran one last year, so we do purple teaming.” A single exercise is a snapshot. The value is the trend across exercises and the continuous automation that holds coverage between them (Parts 11-12).
Final Revision / Summary
Section titled “Final Revision / Summary”The exercise is the event that uses every capability. Chapter 1’s loop, Chapter 2’s emulation, and Chapter 3’s detection engineering converge in a planned, scoped, time-boxed exercise that starts with an objective and ends with owned, dated remediation and a trend line.
Roles stay distinct even when people don’t. Exercise Director (scope, go/no-go, abort), White Cell (deconfliction), Scribe (the record), Red (fire techniques), Blue (detect, tune, re-test). The role never to collapse is the Scribe — the timeline is the raw material of the report.
Transparency is the defining feature. Red announces, blue reports what they see, both diagnose gaps together, and logged-only gaps get fixed in-session. That minute-scale loop, not a shared report, is what makes it purple.
Choose the format deliberately. Tabletop (process), technique-driven (build coverage), adversary-emulation (stress-test coverage against a real actor). Do them in that order; emulation before technique coverage exists just adds unattributable noise.
Scope in writing, safety by design. A signed RoE with explicit in/out host lists, forbidden actions, blast-radius limits, deconfliction, and pre-named abort triggers. Snapshot before anything fires; mark exercise activity; verify a clean return to baseline including re-enabled logging.
Plan from intelligence. Turn the actor most likely to target you into an ordered, kill-chain-phased technique list, each row annotated with the data source and the predicted outcome — so a surprise (predicted alerted, got nothing) surfaces a broken detection.
Score on five dimensions. Detection outcome, MTTD, robustness, response quality, and in-session-fixed — not a single pass/fail. Report alerted % and covered-at-L3+ % together; the gap between them is the honest picture.
The report’s job is owned, dated remediation. Every gap: type, fix, named owner, priority by risk, retest date. A remediation is not done until a retest re-fires the technique and confirms the fix at the target robustness.
Trend beats snapshot. Across exercises, watch covered-at-L3+ % (real coverage) and remediation close rate (whether the program compounds). Close rate, not find rate, predicts next year’s score.
Mature = continuous. Automated emulation regression-tests known coverage nightly and catches silent rot; detection-as-code fires the technique on every rule merge. Automation holds the line; human exercises against fresh actors advance it.
You can start small. Two people, free tools, a shared timeline doc, and a two-hour focused session every fortnight builds more real coverage than one exhausting all-day sprint that measures everything and fixes nothing.
Cheat Sheet / Quick Reference
Section titled “Cheat Sheet / Quick Reference”Exercise Director objective, scope, go/no-go, ABORTWhite Cell deconfliction, in-scope confirmationScribe the timeline (never skip)Red Lead/Operators fire techniques, capture exact cmd+outputBlue Lead/Analysts detect, record outcome, tune + re-test liveFormat choice
Section titled “Format choice”Tabletop test process/roles/decisions (low cost)Technique-driven build + measure coverage breadth (medium)Adversary-emul stress-test coverage vs a real actor (high)Order: technique-driven FIRST, emulation to stress-test.RoE must-haves
Section titled “RoE must-haves”[ ] authorizing signature + window [ ] in-scope AND explicitly-out host lists[ ] objective + measurable success [ ] forbidden actions (destructive/PII/DoS)[ ] snapshot owner [ ] blast-radius limits (hop cap, synthetic data)[ ] deconfliction contact (reachable) [ ] named abort triggers + procedurePer-technique loop
Section titled “Per-technique loop”RED announce -> WHITE confirm in-scope + timestamp -> RED fire (capture cmd+output)-> BLUE check telemetry (logged? alerted? MTTD?) -> record outcome-> if logged-only gap: diagnose together, deploy/tune rule, RED RE-FIRE, validateScribe record per technique
Section titled “Scribe record per technique”technique_id | procedure | timestamp | source_host | target | exact_commandtelemetry_seen | outcome(not-logged/logged-only/alerted/prevented) | MTTDrobustness(L1-4) | detection_ref | in-session_fix? | evidenceScoring dimensions
Section titled “Scoring dimensions”outcome not-logged / logged-only / alerted / preventedMTTD seconds to alertrobustness L1 string -> L2 tool -> L3 technique-invariant -> L4 behaviouralresponse none / manual / playbook / automatedfixed? in-session yes/noReport: alerted % AND covered-at-L3+ % (never one alone)Gap type -> fix
Section titled “Gap type -> fix”not-logged -> onboard data source / enable audit policy (cannot fix in-session)logged-only -> write/tune a rule (often fixable in-session)low-robustness-> climb the ladder to L3+no-response -> playbook + routingprevention -> deploy control AND confirm it alertsAfter-action remediation row (owner + retest MANDATORY)
Section titled “After-action remediation row (owner + retest MANDATORY)”finding | gap_type | fix | OWNER | priority(by risk) | RETEST_DATEclosed = retest re-fires the technique and confirms alert at target robustnessProgram trend metrics
Section titled “Program trend metrics”alerted % | covered-at-L3+ % | median MTTD | in-session fix rateremediation CLOSE rate (predicts improvement) | regression count (rot)Glossary
Section titled “Glossary”| Term | Meaning |
|---|---|
| White Cell | Neutral control/deconfliction group overseeing the exercise |
| Scribe | Role owning the real-time timeline and evidence record |
| RoE | Rules of Engagement: signed scope, limits, abort, deconfliction |
| Deconfliction | Keeping the real SOC able to tell exercise from real incident |
| Blast radius | The bounded extent of an exercise’s real-world effect |
| In-session fix | A gap diagnosed and turned into a validated alert during the run |
| Technique-driven exercise | Firing individual ATT&CK techniques to build/measure coverage |
| Adversary emulation | Emulating a specific actor’s chained TTPs end to end |
| MTTD / MTTR | Mean time to detect / respond |
| Covered-at-L3+ | Alerted at technique-invariant or behavioural robustness |
| Retest | Re-firing a technique on the retest date to confirm a fix |
| Close rate | Retested-closed findings / total findings |
| Continuous purple teaming | Scheduled automated emulation + detection-as-code CI |
Practice Labs & Resources
Section titled “Practice Labs & Resources”- Run a full two-hour technique-driven exercise in DetectionLab. Take one ATT&CK tactic (Credential Access), build an 8-technique plan with predicted outcomes (Part 4.2), fire each with Atomic Red Team, record the scorecard live in a shared doc, fix every logged-only gap in-session, and write the one-page after-action report with owners and retest dates. Then actually do the retests two weeks later.
- Write and sign a real RoE (Part 3.1) for your own lab before the exercise above. Practising the scope-and-safety discipline on a lab is how it becomes muscle memory for a production engagement.
- MITRE CTID Adversary Emulation Library. Take a published plan (APT29, FIN6, Carbanak+FIN7), extract its applicable techniques, and run an emulation exercise. Compare the chained result against the technique-by-technique baseline you built earlier — this is the “technique-driven first, emulation to stress-test” progression from Part 2 made real.
- Build the scorecard + Navigator trend. Use the Part 7.3 script and Chapter 3’s coverage-layer generator to produce before/after Navigator layers for one exercise, then run a second exercise a month later and diff them. Seeing coverage move is the single most motivating artifact for a program.
- Practise the abort. In the lab, deliberately trigger an abort mid-exercise (simulate “blast radius exceeded”), execute the Part 5.3 protocol, and time the return to clean state. An abort you have rehearsed is one you can trust.
- Two-person purple drill. With one colleague, run the compressed model from Part 9.1: you fire, they detect, both edit the timeline. Prove to yourselves that a valuable exercise needs no cell structure and no commercial platform.
- Stand up continuous regression. Schedule an automated Atomic/CALDERA run of your covered technique set (Part 12), wire the “still alerting?” check to file a ticket on failure, and deliberately break a rule to confirm the regression fires. This is the program-level version of Chapter 3’s detection-as-code CI.
- VECTR end to end. Log two or three exercises in VECTR (community edition) to build the trend metrics from Part 11 — covered-at-L3+ %, MTTD, and close rate over time — and use it to drive the next exercise’s plan.
- TryHackMe — “Purple Team”, “Red Team Fundamentals”, “Intro to Detection Engineering” and HackTheBox purple/blue tracks for guided practice combining execution, detection, and reporting in one flow.
- Read published purple/exercise write-ups and IR retrospectives, and reverse-engineer a technique plan from a real incident: “if this actor did exactly what the report says, which of my detections would fire?” That question is the seed of your next exercise.
This completes the Purple Team notebook. Across four chapters it moved from the collaborative philosophy, through the offensive emulation engines and the defensive detection-engineering discipline, to the structured exercise that fuses them into a measured, trended, continuously improving capability. The next notebook steps up from operating defences to governing them — the frameworks, risk management, and architecture that decide what a security program is trying to achieve in the first place.
Discussion
Questions, corrections, or something to add? Comment with your GitHub account.