Skip to content

Chapter 36: Purple Team Operations — Continuous Detection Improvement

Overview

Purple teaming is the structured, collaborative methodology where offensive (red) and defensive (blue) teams work together in real-time to measure, validate, and improve detection and response capabilities. Unlike traditional red-vs-blue engagements, purple team operations are transparent, iterative, and outcome-focused — the goal is not to expose gaps secretly but to close them immediately. This chapter covers purple team frameworks, VECTR-based tracking, detection engineering feedback loops, atomic testing with Atomic Red Team, and purple team program maturity.

Learning Objectives

  • Distinguish purple team operations from traditional red team engagements
  • Design and execute a structured purple team exercise using VECTR
  • Run Atomic Red Team tests and map results to detection gaps
  • Build a detection engineering feedback loop from purple team findings
  • Implement continuous purple team automation with Caldera and Atomic
  • Measure purple team program effectiveness with detection coverage metrics

Prerequisites

  • Chapter 5 (Detection Engineering)
  • Chapter 17 (Red Team Operations)
  • Chapter 9 / 28 (Incident Response)
  • Familiarity with SIEM query languages (Sigma, KQL, SPL)

Why Purple Teams Fail

Most purple team programs fail because findings never get converted into detection rules. Red team identifies a technique, blue team says "yes we missed that," and the exercise ends. Without a structured feedback loop converting every missed technique into a new detection, the program delivers no lasting security improvement. This chapter focuses on the feedback loop above all else.


36.1 Purple Team vs. Red Team

flowchart LR
    subgraph Red["Traditional Red Team"]
        R1[Silent assessment\nno blue team\nawareness] --> R2[Final report\n6-8 weeks] --> R3[Findings list\nno fix timeline]
    end
    subgraph Purple["Purple Team"]
        P1[Collaborative\nexecution] --> P2[Real-time detection\nvalidation] --> P3[Immediate rule\ndevelopment] --> P4[Re-test &\nvalidate fix] --> P1
    end
    style Red fill:#ff7b7222,stroke:#ff7b72
    style Purple fill:#3fb95022,stroke:#3fb950
Dimension Red Team Purple Team
Blue team awareness No Yes — collaborative
Finding disclosure End of engagement Real-time
Detection development Out of scope Core deliverable
Cadence Annual/quarterly Weekly to continuous
Primary output Vulnerabilities Detection rules
ROI measurement Exposure found Detection coverage delta
ATT&CK coverage Measured post-hoc Measured continuously

36.2 Purple Team Frameworks

TIBER-EU / TIBER-XX

TIBER (Threat Intelligence-Based Ethical Red Teaming) is the European Central Bank's framework for financial sector purple teaming, adopted by EU member states. It mandates threat-intelligence-driven scenarios based on named threat actors relevant to the institution.

TIBER-EU phases: 1. Preparation — Scope definition, NDA, test team selection 2. Testing — Threat intelligence phase → red team test phase (12+ weeks) 3. Closure — Purple team remediation phase, attestation

CBEST (UK) / CORIE (Australia)

Similar intelligence-led frameworks for financial sector regulators. All share the requirement that scenarios be based on genuine threat actor intelligence specific to the industry.

PTES (Penetration Testing Execution Standard)

More generic framework adaptable to purple team use with added detection tracking.


36.3 VECTR — Tracking Purple Team Exercises

VECTR is an open-source platform for tracking red and purple team exercises, mapping them to ATT&CK, and measuring detection coverage over time.

# Deploy VECTR with Docker Compose
git clone https://github.com/SecurityRiskAdvisors/VECTR
cd VECTR
docker compose up -d

# Default access: http://localhost:8081
# Admin credentials set in docker-compose.yml

VECTR Workflow:

sequenceDiagram
    participant RT as Red Team
    participant BT as Blue Team
    participant VECTR

    RT->>VECTR: Create campaign (e.g., "APT29 emulation Q2")
    RT->>VECTR: Add test cases (ATT&CK techniques)
    RT->>BT: Execute technique
    BT->>VECTR: Record detection outcome
    Note over VECTR: Detected / Not Detected / Blocked
    BT->>BT: Write detection rule for gaps
    BT->>RT: Request re-test
    RT->>BT: Re-execute technique
    BT->>VECTR: Update outcome → Detected
    VECTR->>VECTR: Update ATT&CK coverage metrics

VECTR outcome states:

Outcome Definition Next Action
Detected & Blocked Prevention control stopped execution Document and maintain
Detected Alert fired; SOC aware Tune for confidence/fidelity
Not Detected No alert generated Write detection rule
Detected Post-Hoc Found in log review, no alert Convert to real-time rule
N/A Technique not applicable to environment Document exemption reason

36.4 Atomic Red Team — Atomic Testing

Atomic Red Team is a library of small, focused tests (atomics) mapped to ATT&CK techniques. Each atomic is the minimal implementation to exercise a technique.

# Install Invoke-AtomicRedTeam (PowerShell)
Install-Module -Name invoke-atomicredteam -Scope CurrentUser -Force

# Import and install prerequisites
Import-Module Invoke-AtomicRedTeam
Invoke-AtomicTest All -GetPrereqs

# List tests for a technique
Invoke-AtomicTest T1003.001 -ShowDetailsBrief

# Execute LSASS dump test (Test 1 only)
Invoke-AtomicTest T1003.001 -TestNumbers 1

# Check for generated artifacts
Invoke-AtomicTest T1003.001 -CheckPrereqs

# Cleanup after test
Invoke-AtomicTest T1003.001 -Cleanup

Automated purple team script — test entire tactic:

#!/usr/bin/env python3
"""
Purple team automation: run Atomic tests, check SIEM for alerts,
report coverage gaps.
"""
import subprocess
import time
import json
import requests
from datetime import datetime, timedelta

TECHNIQUES_TO_TEST = [
    "T1003.001",  # LSASS Memory
    "T1558.003",  # Kerberoasting
    "T1053.005",  # Scheduled Task
    "T1059.001",  # PowerShell
    "T1550.002",  # Pass the Hash
    "T1490",      # Inhibit System Recovery
]

SIEM_API = "https://sentinel.example.com/api/query"
SIEM_TOKEN = "Bearer <token>"
WAIT_SECONDS = 90  # Time to wait for alert propagation

def run_atomic(technique: str) -> dict:
    result = subprocess.run(
        ["pwsh", "-Command",
         f"Import-Module Invoke-AtomicRedTeam; "
         f"Invoke-AtomicTest {technique} -TestNumbers 1"],
        capture_output=True, text=True, timeout=120
    )
    return {"technique": technique, "executed": result.returncode == 0,
            "output": result.stdout[:500]}

def check_siem_alert(technique: str, start_time: datetime) -> bool:
    """Query SIEM for alerts related to this ATT&CK technique."""
    query = f"""
    SecurityAlert
    | where TimeGenerated > datetime('{start_time.isoformat()}')
    | where ExtendedProperties contains '{technique}'
       or Tactics contains '{technique[:2]}'
    | count
    """
    resp = requests.post(SIEM_API,
                         headers={"Authorization": SIEM_TOKEN},
                         json={"query": query})
    count = resp.json().get("tables", [{}])[0].get("rows", [[0]])[0][0]
    return count > 0

results = []
for tech in TECHNIQUES_TO_TEST:
    print(f"\n[*] Testing {tech}...")
    start = datetime.utcnow()
    exec_result = run_atomic(tech)
    print(f"    Executed: {exec_result['executed']}")
    print(f"    Waiting {WAIT_SECONDS}s for SIEM alert propagation...")
    time.sleep(WAIT_SECONDS)
    detected = check_siem_alert(tech, start)
    outcome = "DETECTED" if detected else "GAP"
    print(f"    Outcome: {outcome}")
    results.append({"technique": tech, "executed": exec_result["executed"],
                    "detected": detected, "outcome": outcome})
    # Cleanup
    subprocess.run(["pwsh", "-Command",
                    f"Import-Module Invoke-AtomicRedTeam; Invoke-AtomicTest {tech} -Cleanup"],
                   capture_output=True)

# Report
gaps = [r for r in results if r["outcome"] == "GAP"]
detected = [r for r in results if r["outcome"] == "DETECTED"]
print(f"\n{'='*50}")
print(f"Purple Team Results — {datetime.utcnow().strftime('%Y-%m-%d')}")
print(f"Detected: {len(detected)}/{len(results)} ({len(detected)/len(results)*100:.0f}%)")
print(f"Gaps: {len(gaps)}")
for g in gaps:
    print(f"  GAP: {g['technique']} — Create detection rule")
print(json.dumps(results, indent=2))

36.5 Detection Engineering Feedback Loop

The core value of purple team operations is the automatic generation of detection rules for every gap found.

flowchart TD
    EXEC[Execute Atomic\nTechnique] --> CHECK{Alert\nGenerated?}
    CHECK -->|Yes| TUNE[Review fidelity\nReduce FP rate]
    CHECK -->|No| HUNT[Threat Hunt\nfor artifacts]
    HUNT --> FOUND{Evidence\nFound?}
    FOUND -->|Yes| RULE[Write Detection\nRule from Artifacts]
    FOUND -->|No| TELEMETRY[Check telemetry\ngaps — log source?]
    TELEMETRY --> INGEST[Add missing\nlog source]
    INGEST --> RULE
    RULE --> SIGMA[Convert to\nSigma format]
    SIGMA --> TEST[Test rule against\nhistorical data]
    TEST --> DEPLOY[Deploy to SIEM\nproduction]
    DEPLOY --> RETEST[Re-run Atomic\n48h later]
    RETEST --> CHECK
    TUNE --> CLOSE[Close in VECTR\nDetected ✓]

    style RULE fill:#58a6ff22,stroke:#58a6ff
    style CLOSE fill:#3fb95022,stroke:#3fb950

Sigma Rule from Purple Team Finding

Scenario: Atomic T1003.001 (LSASS dump via ProcDump) was not detected.

Step 1 — Hunt for artifacts:

# Splunk: find ProcDump LSASS access
index=wineventlog EventCode=10 TargetImage="*lsass.exe"
| stats count by SourceImage, TargetImage, CallTrace

# KQL (Sentinel)
SecurityEvent
| where EventID == 4656
| where ObjectName contains "lsass"
| where ProcessName !in ("C:\\Windows\\System32\\werfault.exe",
                          "C:\\Windows\\System32\\taskmgr.exe")

Step 2 — Write Sigma rule:

title: LSASS Memory Access via ProcDump or Sysinternals
id: c3b4f1e2-8a7d-4c9b-b5e1-f2a3d4e5f6g7
status: stable
description: Detects LSASS memory access by non-system processes, indicating credential dumping
references:
  - https://attack.mitre.org/techniques/T1003/001/
author: Purple Team — Q2 Exercise
date: 2026-03-17
tags:
  - attack.credential_access
  - attack.t1003.001
logsource:
  category: process_access
  product: windows
detection:
  selection:
    TargetImage|endswith: '\lsass.exe'
    GrantedAccess|contains:
      - '0x1010'
      - '0x1410'
      - '0x1438'
      - '0x143a'
      - '0x1418'
  filter_legit:
    SourceImage|startswith:
      - 'C:\Windows\System32\'
      - 'C:\Windows\SysWOW64\'
      - 'C:\Program Files\CrowdStrike\'
      - 'C:\Program Files\SentinelOne\'
  condition: selection and not filter_legit
falsepositives:
  - Legitimate monitoring tools (tune filter_legit per environment)
  - EDR products (add vendor paths to filter)
level: high

Step 3 — Backtest against historical data:

# Validate no false positives in last 30 days
sigma convert -t splunk rules/lsass-access.yml | \
  xargs -I{} splunk search {} earliest=-30d | wc -l
# If >0, review and tune filter_legit paths


36.6 ATT&CK Coverage Measurement

#!/usr/bin/env python3
"""
Calculate detection coverage against ATT&CK Enterprise matrix.
Uses VECTR API or local CSV export.
"""
import csv
import json
from collections import defaultdict

# Load VECTR exercise results
VECTR_EXPORT = "vectr_results.csv"
# Load ATT&CK Enterprise techniques (download from MITRE CTI)
ATTACK_FILE = "enterprise-attack-techniques.json"

def load_vectr(path):
    results = {}
    with open(path) as f:
        for row in csv.DictReader(f):
            tid = row.get("Technique ID", "").strip()
            outcome = row.get("Outcome", "").strip()
            if tid:
                results[tid] = outcome
    return results

def coverage_report(vectr_results, attack_techniques):
    tactic_coverage = defaultdict(lambda: {"total": 0, "detected": 0, "tested": 0})
    for tech in attack_techniques:
        tid = tech["external_id"]
        for tactic in tech["tactics"]:
            tactic_coverage[tactic]["total"] += 1
            if tid in vectr_results:
                tactic_coverage[tactic]["tested"] += 1
                if vectr_results[tid] in ("Detected", "Detected & Blocked"):
                    tactic_coverage[tactic]["detected"] += 1
    print(f"\n{'Tactic':<30} {'Tested':>8} {'Detected':>10} {'Coverage':>10}")
    print("-" * 62)
    for tactic, stats in sorted(tactic_coverage.items()):
        pct = (stats["detected"] / stats["total"] * 100) if stats["total"] else 0
        print(f"{tactic:<30} {stats['tested']:>8} {stats['detected']:>10} {pct:>9.1f}%")

# Example output:
# Tactic                         Tested   Detected   Coverage
# ----------------------------------------------------------
# Credential Access                   8          6      75.0%
# Defense Evasion                    12          4      33.3%
# Lateral Movement                    6          5      83.3%

36.7 Continuous Purple Teaming with CALDERA

MITRE CALDERA is an adversary emulation platform enabling automated continuous purple team testing.

# CALDERA ability definition (YAML) — Kerberoasting
id: f3b7c2e1-4d8a-9b5c-e2f1-a3b4d5e6f7g8
name: Kerberoast service accounts
description: Request TGS tickets for all SPNs in the domain
tactic: credential-access
technique:
  attack_id: T1558.003
  name: Kerberoasting
platforms:
  windows:
    psh:
      command: |
        Import-Module ActiveDirectory;
        Get-ADUser -Filter {ServicePrincipalName -ne "$null"} -Properties ServicePrincipalName |
        ForEach-Object {
          $spn = $_.ServicePrincipalName[0];
          Add-Type -AssemblyName System.IdentityModel;
          New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList $spn
        };
        klist
      cleanup: klist purge
      parsers:
        - module: app.parsers.kerberoast
          parserconfigs:
            - source: host.collected.kerberos_tickets
# Deploy CALDERA
git clone https://github.com/mitre/caldera.git --recursive
cd caldera
pip install -r requirements.txt
python server.py --insecure  # Use TLS in production

# API: create operation programmatically
curl -X POST http://localhost:8888/api/v2/operations \
  -H "Authorization: Bearer $CALDERA_API_KEY" \
  -d '{
    "name": "Purple Team - Lateral Movement",
    "adversary": {"adversary_id": "apt29-profile"},
    "group": "blue-team-endpoints",
    "auto_close": true,
    "obfuscator": "plain-text",
    "visibility": 51
  }'

36.8 Purple Team Report Template

# Purple Team Exercise Report
**Date:** YYYY-MM-DD | **Duration:** X days | **Analyst:** Name

## Executive Summary
- Techniques tested: N
- Detected: N (N%)
- Detection gaps closed: N
- New Sigma rules created: N
- MITRE ATT&CK coverage delta: +N%

## Test Results Summary

| Technique | Name | Outcome | Rule Written |
|-----------|------|---------|--------------|
| T1566.001 | Spearphishing | Detected | N/A |
| T1003.001 | LSASS Dump | Not Detected | ✓ lsass-access.yml |
| T1558.003 | Kerberoasting | Detected Post-Hoc | ✓ kerberoast-tgs.yml |

## Detection Gaps and Remediations

### GAP-001: LSASS Memory Access (T1003.001)
- **Finding:** No alert for ProcDump accessing lsass.exe
- **Root Cause:** Sysmon Event 10 not collected for this host group
- **Fix Applied:** Sysmon config updated; Sigma rule deployed
- **Re-test Result:** Detected ✓
- **VECTR Updated:** 2026-03-17

## ATT&CK Coverage Heatmap
[Insert VECTR navigator export]

## Recommendations
1. Enable Sysmon on all server endpoints (currently only workstations)
2. Increase log retention for process creation events (currently 14 days → 90 days)
3. Tune credential access alerts — current FP rate 40% (target: <5%)

Exam Prep & Certifications

Relevant Certifications

The topics in this chapter align with the following certifications:

  • GIAC GXPN — Domains: Advanced Penetration Testing, Exploit Research
  • OSCP — Domains: Penetration Testing, Active Directory, Exploitation

View full Certifications Roadmap →

Nexus SecOps Benchmark Controls

Control ID Description Validation
Nexus SecOps-PT-01 Purple team exercises conducted quarterly with VECTR tracking VECTR campaign records; outcomes documented
Nexus SecOps-PT-02 Every detection gap generates a Sigma rule within 5 business days Sigma rule repository; VECTR re-test confirmation
Nexus SecOps-PT-03 Atomic Red Team baseline coverage ≥ 60% of applicable techniques Automated atomic test report; SIEM alert evidence
Nexus SecOps-PT-04 ATT&CK coverage measured and reported to leadership monthly Coverage dashboard; tactic-level heatmap
Nexus SecOps-PT-05 Detection rules backtested on 30-day historical data before deploy FP rate ≤ 5% for new detection rules
Nexus SecOps-PT-06 CALDERA or equivalent enables continuous automated testing Automated test scheduler; weekly coverage report

Key Terms

Atomic Red Team — Library of small, focused ATT&CK-mapped test cases enabling repeatable technique execution; maintained by Red Canary.

CALDERA — MITRE's open-source adversary emulation platform supporting autonomous and manual operation execution.

Detection Coverage — The percentage of ATT&CK techniques for which reliable detection rules exist and have been validated.

False Positive Rate — The proportion of alerts fired on benign activity; a key quality metric for detection rules (target: <5%).

Feedback Loop — The process converting purple team findings into detection rules, re-testing, and confirming closure.

Purple Team — Collaborative offensive/defensive exercise focused on improving detection and response rather than exposing vulnerabilities.

Sigma — Open-source generic signature format for SIEM rules; convertible to Splunk SPL, KQL, Elastic EQL, and others.

VECTR — Open-source purple team tracking platform mapping exercises to ATT&CK and measuring detection coverage over time.