MITRE Coverage Beyond the Green Matrix

Why technique heatmaps lie about detection coverage, and how to fix the loop.
detection
intel
engineering
Author
Published

October 21, 2026

I’ve written a few times on this blog about working with MITRE ATT&CK: first on treating the framework as a tool rather than the goal, then on a structured process for mapping detection rules to techniques, and later on running gap analysis with MITRE Navigator. Recently, while reviewing how Graft 🌿 calculates coverage gaps and exports Navigator layers, I stepped back to re-examine a problem I’ve watched security teams repeat for years.

Every SOC talks about MITRE coverage, and every CISO loves a red-yellow-green Navigator slide for the quarterly board deck. Yet in some organizations, the daily reality behind that slide is pure toil: Cyber Threat Intelligence (CTI) analysts spend hours tagging threat reports with technique IDs, detection engineers paste Txxxx strings into rule YAML headers to satisfy a pull request template, and nobody touches those tags again until the next audit.

Even worse is what happens when teams blindly trust the matrix. Coloring a technique cell green because a single rule references its ID creates a false sense of security. Attackers don’t operate at the technique level; they run concrete procedures against specific operating systems, cloud control planes, and network segments. If your rule catches one tool inside a sub-technique while missing three others, or covers Windows while your production workloads run on Linux, that green cell turns coverage into misinformation.

Here is how I think about making MITRE ATT&CK earn its keep in a detection program, why rule-only heatmaps fail, and how I reshaped Graft’s CLI, documentation, and AI skills to reflect an honest, best-effort assessment.

Mapping Without Action Is Just SOC Toil

Attackers don’t plan intrusions by opening the MITRE ATT&CK matrix and picking items from column A and column B. ATT&CK is a human taxonomy built to give defenders (blue teams, red teams, purple teams, and CTI) a shared vocabulary to describe adversary behavior.

Like any taxonomy, it only has value if something downstream consumes it to change decisions. When a team treats ATT&CK tags as passive metadata, the maintenance cost quickly outweighs any benefit. Analysts resent filling out fields that never drive engineering work, mappings rot as rules evolve, and the catalog turns into a graveyard of stale YAML tags.

For the mapping effort to pay off, it has to feed an active engineering loop:

  • Purple team exercises: Selecting techniques relevant to your threat model, emulating concrete procedures in staging or production, verifying which rules fire (and which log sources dropped events on the floor), and turning the post-exercise findings directly into detection backlog items.
  • CTI-driven gap prioritization: Joining the techniques used by threat groups targeting your sector against your active rule catalog to decide what to build next sprint.
  • Rule hardening: Using the taxonomy to inspect adjacent procedures, data components, and tunable thresholds when reviewing or refactoring an existing detection.

If your team isn’t running at least one of those loops, stop forcing analysts to tag rules by hand until you fix the consumption side. As a rule of thumb, consumption drives production because time, especially in a SOC, is precious. 🪙🪙

The Mapping Problem

Even when a detection team wants to use ATT&CK properly, they hit an immediate friction point: mapping rules to tactics and techniques is subjective. Hand the same detection rule to two experienced engineers and you’ll frequently get two different sets of technique IDs, different tactic selections, and even a different number of mappings per rule.

Some of that divergence is healthy. Two analysts reading the same query might spot different attacker tradecraft that the logic catches, and bringing those perspectives together makes the team sharper. Still, there is a big difference between healthy diversity of thought and an uncalibrated free-for-all. Contrary to the common belief that “you can’t really be wrong at MITRE mapping,” bad mappings happen, usually in four ways:

  1. Template and copy-paste rot: An engineer clones an existing rule file to write a new detection, updates the query and description, and forgets to touch the mitre block. Six months later, a GCP IAM rule is still claiming coverage for T1059.001 — PowerShell.
  2. Confusing the symptom, the mechanism, and the downstream goal: Suppose a rule alerts when an atypical parent process spawns whoami. One analyst maps T1033 — System Owner/User Discovery, what the command literally did. A second maps T1190 — Exploit Public-Facing Application, assuming the process is a web shell. A third maps TA0004 — Privilege Escalation, guessing what the attacker wants to do next. When analysts map the entire kill chain they hope to interrupt rather than the specific behavior the rule logic actually checks, a single rule lights up five columns on the matrix and inflates coverage across tactics the rule cannot actually see.
  3. Over-mapping and under-mapping: There is no magic number of techniques per rule, but rule granularity sets a natural ceiling. Atomic rules that watch a single API call or command pattern almost always map to a single technique — 1:1. Behavioral or correlation rules that join events across stages might legitimately map to two or three techniques. In practice, if a single rule maps to more than three techniques, it’s a smell: either the author over-mapped tangential techniques, or the query is a kitchen-sink rule that should be split up. On the flip side, under-mapping (tagging only a broad parent technique like T1059 when the rule only inspects PowerShell T1059.001, or dropping a technique that a correlation rule genuinely proves) biases your data just as badly.
  4. Taxonomy drift: MITRE deprecates, merges, and splits techniques across releases — T1086 moving to T1059.001 is the classic example. Without automated validation against the current STIX bundle in CI, mappings silently go stale.

You can’t eliminate subjectivity with a linter alone, because a linter only knows whether T1033 is a valid STIX ID, not whether your query actually detects it. Aligning a team requires intentionality at authoring time and human calibration over time.

That’s the main reason I made Graft opinionated at the schema level: metadata.mitre is a dictionary keyed by tactic shortname (persistence: ["T1098.001"]), and the linter verifies that every technique listed under a tactic actually belongs to that tactic in STIX. Many techniques span three or four tactics — T1078.004 Cloud Accounts sits under Initial Access, Persistence, Privilege Escalation, and Defense Evasion. If an author only had to drop a bare T1078.004 list into a rule, a simple login alert would light up all four columns on the matrix. Keying techniques under their specific tactic forces the engineer to pause and ask which stage of the intrusion the query actually observes.

Considering detection teams usually work in sprints, the best mechanism I’ve found for team alignment is running quarterly quality sprints as part of the rule lifecycle — or short calibration exercises during detection guild meetings. Pull five existing rules from the repository, hide their metadata blocks, and have every analyst independently map the queries to tactics and techniques for ten minutes. Put the results on a screen and debate the outliers. Why did one person map one technique while another mapped four? Does the query prove initial access, or only discovery? Document those decisions as short, concrete rules of thumb in your repository’s authoring guide — for example: map only what the rule logic actually checks; pick the sub-technique over the parent whenever the query is OS- or mechanism-specific. You’ll never get two analysts to map 100% identically, and you don’t need to, as long as the team is evolving in the same direction.

Empowering rule reviewers matters just as much, since they are the immediate guardrail against bad categorization. I wrote in Detection-as-Code, Then What? about how peer reviews often swing between rubber-stamping and nitpicking typos. The middle way is what works here. A reviewer is signing off on the rule alongside the author, which means sharing responsibility for both the query logic and the metadata, including MITRE mappings. AI is a big help in this loop when backed by a calibrated review skill: the author drafts the rule and runs an AI review, then a peer reviews it — and can run the AI skill again for a second opinion.

The Payoff: Enrichment

Once your team invests the effort to calibrate and maintain clean technique mappings, you get to the real engineering advantage of ATT&CK: the technique ID acts as a foreign key across datasets.

Many teams still think of a MITRE technique page as a static wiki article with a few paragraphs of free text. In MITRE ATT&CK v20.0, the underlying STIX data is a relational graph connecting 706 active Enterprise techniques and sub-techniques to:

flowchart LR
    Rule["Detection Rule (YAML)"] -->|maps to| Tech["Technique (Txxxx.xxx)"]
    Tech -->|used by| Group["Threat Groups (Gxxxx)"]
    Tech -->|detected via| Det["Detection Strategy (DETxxxx)"]
    Tech -->|prevented by| Mit["Mitigations (Mxxxx)"]
    Det -->|contains| An["Behavioral Analytics (ANxxxx)"]
    An -->|requires| DC["Data Components (DCxxxx)"]
    An -->|exposes| Mut["Mutable Elements (Tunables)"]

Because Txxxx.xxx sits at the center of that graph, tagging a rule with a single technique ID immediately joins your local detection catalog with CTI profiles, telemetry requirements, and vendor-agnostic analytic blueprints. When your CTI team delivers a threat landscape study naming the top ten groups targeting your industry, you can cross-reference their Gxxxx profiles against your rules in seconds. Better yet, when an analyst sits down to review a weak rule or write a new one, reading the technique’s linked DETxxxx strategies, ANxxxx analytics, and mutable_elements gives them concrete ideas to harden the query or build companion rules for variants they hadn’t considered.

The takeaway here is simple: invest the time to map rules to techniques accurately, then use the intersections across MITRE’s datasets to find ways to sharpen the rule or apply mitigations.

The Green-Matrix Trap

Joining rule tags against STIX data is powerful, but it’s also where teams walk straight into a trap. They export their mapped rules to a MITRE Navigator layer, see forty techniques colored green, show the slide to leadership, and move on.

A green cell on a rule-only matrix is a half-truth for two structural reasons: the matrix stops at Techniques (TT, not TTP), and the rule catalog knows nothing about your actual infrastructure — the missing inventory.

1. The Procedure Gap (TT vs. TTP)

We say “TTP” all the time, but the ATT&CK matrix only plots Tactics (columns) and Techniques / Sub-Techniques (cells). Procedures (the concrete tools, flags, API calls, and syscalls an adversary runs) live beneath the cell, and there are far too many of them to fit into a two-dimensional grid.

When you color a technique green because one active rule maps to it, you’re equating one procedure with the entire technique. Consider three examples across endpoint, cloud, and network telemetry:

  • Endpoint (T1003.001, LSASS Memory): Suppose you write a rule watching for mimikatz.exe or procdump.exe touching lsass.exe. You tag it T1003.001, and the sub-technique turns green. Tomorrow, an operator dumps LSASS using rundll32.exe comsvcs.dll, MiniDump or reads process memory via direct syscalls — NtReadVirtualMemory. Your rule never fires, despite your “covered” status on the slide.
  • Cloud (T1098.001, Additional Cloud Credentials): You deploy gcp_service_account_key_created to alert when someone creates a user-managed service account key in Google Cloud. MITRE classifies T1098.001 under the IaaS platform, so your IaaS matrix marks T1098.001 green. Meanwhile, your company also runs workloads in AWS, and you have zero rules watching CreateAccessKey or ImportKeyPair in CloudTrail.
  • Network (T1046, Network Service Discovery): You have a firewall rule (multiple_ports_scanned) that alerts when a source IP hits more than twenty DENY ports inside five minutes. It catches a noisy scan against closed ports, turning T1046 green. If an attacker scans ports that are actually open (ALLOW events) or paces the scan below your five-minute threshold, the rule is blind.

2. The Environment and Shadow IT Gap

Even if your rules covered every common procedure for a technique on Windows, that tells you nothing about whether your environment also runs Linux, macOS, Kubernetes, or SaaS identity providers where those rules don’t apply.

Worse, a rule repository only reflects what you intend to monitor, not what exists in production. Suppose you have a solid set of AWS CloudTrail detections in your repository, mapped and tested. Last month, a product acquisition or an engineering team spun up a new AWS organization (or a shadow GCP project) that nobody wired into the SIEM ingestion pipeline. Your rules are active, your Navigator layer is bright green, and the logs from that environment aren’t even arriving.

Important

A high-confidence coverage assessment requires three pillars of data:

  1. Detection Rule Data: Which rules are active in production (status: prod, enabled: true), what logic and log sources they actually evaluate, and how their parent/sub-technique mappings roll up.
  2. Threat Intelligence (CTI) Data: Which adversary groups (Gxxxx), campaigns, and concrete procedures target your sector, alongside the detection strategies (DETxxxx), behavioral analytics (ANxxxx), and data components (DCxxxx) required to spot them.
  3. Environment Scope (Inventory): An authoritative inventory of the operating systems, cloud accounts, identity providers, and active log pipelines running in production, including visibility into where telemetry is missing or broken. Here, just like I wrote in Prepare Your SOC for AI, I have to be honest: I’ve never seen a company with a complete inventory like this, only best efforts here and there, so treat this as a direction rather than a reality.

Without an inventory, any tool that only reads your rule repository is operating with a partial view. Thinking in terms of an asset inventory also debunks the myth that a detection team’s goal should be having at least one rule per technique. That only holds if the technique is actually in scope for your environment. Otherwise, leaving it blank is completely fine, and reports to senior management should make that explicit.

How Graft Implements It

When I reviewed how Graft handled MITRE coverage and gap analysis, I wanted the code, the CLI output, and the documentation to be honest about those three pillars. Graft has Pillar 1 (your Detection-as-Code repository) and a large slice of Pillar 2 (MITRE’s offline STIX v20.0 graph of techniques, threat groups, strategies, analytics, and data components), but as an offline CLI tool, it doesn’t have Pillar 3 — the inventory.

I made four concrete design choices to get as close as possible to an honest assessment without pretending a green cell means you’re safe.

1. Never Count Disabled Rules as Coverage

First, a rule sitting in git with enabled: false (or status: test when evaluating production posture) provides zero protection during an intrusion. In Graft, graft mitre gaps, graft mitre inspect, and graft export navigator --include-gaps strictly exclude disabled rules from active coverage counts. If your only rule for T1059.001 is disabled, the technique stays uncovered.

2. Strict 3-State Parent/Sub-Technique Roll-Ups

Second, ATT&CK’s two-level hierarchy (Txxxx parent techniques and Txxxx.xxx sub-techniques) makes naive set math lie. If T1098 (Account Manipulation) has seven sub-techniques and you only have a rule for T1098.001 (Additional Cloud Credentials), what is the state of T1098? Calling it covered hides six unmonitored sub-techniques; calling it uncovered ignores your rule on T1098.001.

I formalized a strict 3-state roll-up (covered, partial, uncovered) across both graft mitre gaps and Navigator exports:

  • A sub-technique (Txxxx.xxx) is covered if it is directly mapped by an active rule, partial if it is unmapped by name but an active rule maps broadly to its parent Txxxx, and uncovered otherwise.
  • A parent technique (Txxxx) is covered only when directly mapped by an active rule or when every in-scope sub-technique beneath it is covered. If some, but not all, of its sub-techniques are covered, the parent rolls up as partial (scored 50 in yellow #ffd166 on Navigator layers, versus 100 in green #06d6a0 for covered).

You can see this roll-up directly when querying persistence gaps for IaaS:

$ uv run graft mitre gaps --platform IaaS --tactic persistence --top 6
MITRE ATT&CK v20.0 Gap Analysis [platform=IaaS, tactic=persistence]
In Scope: 15 | Covered: 2 | Partial: 2 | Uncovered: 11 (Showing 6 gaps)
================================================================================================================
Technique ID  State      Groups  Technique Name                 Strategies   Data Components
----------------------------------------------------------------------------------------------------------------
T1078         partial    49      Valid Accounts                 DET0560      DC0002, DC0032, DC0067
T1098         partial    6       Account Manipulation           DET0096      DC0010, DC0032, DC0061
T1078.001     uncovered  4       Default Accounts               DET0465      DC0002, DC0067, DC0088
T1098.003     uncovered  3       Additional Cloud Roles         DET0277      DC0010
T1098.004     uncovered  3       SSH Authorized Keys            DET0126      DC0032, DC0061, DC0064
T1136         uncovered  3       Create Account                 DET0583      DC0010, DC0014, DC0032

Because the repository has active rules for T1078.004 (Cloud Accounts) and T1098.001 (Additional Cloud Credentials), their parent techniques (T1078 and T1098) surface as partial rather than vanishing from the gap report.

3. Bridging the Missing Inventory

Because Graft cannot query your asset inventory, running graft mitre gaps without flags evaluates all 706 active Enterprise techniques, including macOS, mobile-adjacent, or ICS/ESXi platforms you might not even run. To scope the denominator, operators have two options:

  1. Supply inventory and CTI context explicitly via --platform (Windows, Linux, IaaS, Office Suite), --group (G0016, APT29), --tactic, or --data-component — DC0032.
  2. Guess scope from the existing catalog (--infer-scope): When you don’t pass explicit platform filters, --infer-scope inspects the techniques already mapped by your active rules, extracts their MITRE platforms (inferred_platforms) and data components (inferred_data_components), and restricts the gap denominator to techniques sharing those platforms:
$ uv run graft mitre gaps --infer-scope --top 5
MITRE ATT&CK v20.0 Gap Analysis [infer_scope=true]
In Scope: 597 | Covered: 7 | Partial: 12 | Uncovered: 578 (Showing 5 gaps)
================================================================================================================
Technique ID  State      Groups  Technique Name                 Strategies   Data Components
----------------------------------------------------------------------------------------------------------------
T1105         uncovered  90      Ingress Tool Transfer          DET0060      DC0032, DC0039, DC0064
T1204.002     uncovered  87      Malicious File                 DET0294      DC0032, DC0039, DC0059
T1059.001     uncovered  86      PowerShell                     DET0455      DC0016, DC0032, DC0034
T1566.001     uncovered  79      Spearphishing Attachment       DET0236      DC0032, DC0038, DC0039
T1059.003     uncovered  73      Windows Command Shell          DET0202      DC0016, DC0029, DC0032

I also document the exact blind spot of --infer-scope right in the CLI and analyst guide: inferring scope from existing rules can only discover platforms you have mapped at least once. If your company runs a Linux fleet or a second cloud provider with zero rules in the repo, --infer-scope will never know that platform exists.

4. Drilling Below the Technique

Finally, to move past the TT-only matrix and audit procedure-level depth, graft mitre inspect <ID> --analytics pulls the STIX v20.0 DETxxxx detection strategies, ANxxxx behavioral analytics, log sources, and mutable_elements straight to the terminal:

$ uv run graft mitre inspect T1098.001 --analytics --platform IaaS
[TECHNIQUE] T1098.001 — Additional Cloud Credentials
------------------------------------------------------------------------
Status:            active
Tactics:           persistence, privilege-escalation
Platforms:         IaaS, Identity Provider, SaaS
Coverage State:    covered (Local Rules: gcp_service_account_key_created)
Strategies:        DET0531 (Detection Strategy for Additional Cloud Credentials in IaaS/IdP/SaaS)
Data Components:   DC0010 (User Account Modification); DC0066 (Active Directory Object Modification); DC0087 (Active Directory Object Creation)
Mitigations:       M1018 (User Account Management); M1026 (Privileged Account Management); M1030 (Network Segmentation); M1032 (Multi-factor Authentication); M1042 (Disable or Remove Feature or Program)
Threat Groups:     1 total (G1053 (Storm-0501))
Analytics:
  • AN1470 [IaaS]: Cloud API usage to create/import SSH keys or generate new access keys (CreateAccessKey, ImportKeyPair, CreateLoginProfile) from non-console access or unusual principals.
      Log Source: AWS:CloudTrail (CreateAccessKey, ImportKeyPair, CreateLoginProfile, CreateKeyPair) [DC0087]
      Log Source: gcp:audit (iam.serviceAccounts.keys.create, os-login.sshPublicKeys.add) [DC0010]
      Tunable (CallerIdentityContext): Track root, federated identities, and STS tokens separately.
      Tunable (NewCredentialUsageWindow): Time between key creation and first use (default: 5 min).
      Tunable (IAMRoleBaseline): Expected services/accounts allowed to create keys.

Look at what that single output exposes. The top line says Coverage State: covered (Local Rules: gcp_service_account_key_created). On a standard Navigator matrix, you would see a green box and move on. Right underneath, AN1470 [IaaS] shows that covering this sub-technique in IaaS involves both gcp:audit (iam.serviceAccounts.keys.create and os-login.sshPublicKeys.add) and AWS:CloudTrail (CreateAccessKey, ImportKeyPair, CreateLoginProfile, CreateKeyPair), plus three tunable parameters — CallerIdentityContext, NewCredentialUsageWindow, IAMRoleBaseline.

This is also where pairing a deterministic CLI with an AI agent pays off, a pattern I baked into Graft’s /analyze-coverage agent skill. If you ask an ungrounded LLM to “analyze our MITRE coverage,” it will guess technique IDs, hallucinate threat group counts, and praise whatever rules it finds. Instead, /analyze-coverage forbids the agent from reading raw STIX files or doing taxonomy math in its head. The agent has to run graft --json mitre coverage, gaps, and inspect --analytics first, letting deterministic Python code handle the STIX graph joins and 3-state roll-ups. Then the LLM does what it’s actually good at: reading the YARA-L rule logic (gcp_service_account_key_created.yaml) side-by-side with AN1470’s procedures and mutable_elements to tell the engineer, “Your cell is green, but you only monitor GCP service account keys. You have no coverage for GCP OS Login SSH keys or AWS CloudTrail access key creation.”

Closing Thoughts

MITRE ATT&CK is one of the most useful tools we have for aligning threat intelligence, detection engineering, and purple teaming around a shared language. Still, the framework can’t save you from treating a two-dimensional heatmap as ground truth.

If you want your ATT&CK mappings to drive real security outcomes instead of SOC toil:

  • Use them to drive action, establishing a common vocabulary and communicating laterally across teams and upstream to leadership.
  • Calibrate your analysts regularly with short team exercises so everyone maps what the rule logic proves rather than the kill chain they hope to catch.
  • Use technique IDs as relational foreign keys to pull in threat group priorities (Gxxxx), detection strategies (DETxxxx), behavioral analytics (ANxxxx), and required log sources (DCxxxx).
  • Bring environment scope into the denominator so you know which operating systems and cloud accounts actually exist in production, and which ones aren’t shipping logs.
  • Treat covered as the start of the conversation, not the end. Whenever a high-priority technique turns green on your matrix, look underneath the technique at the concrete procedures and tunable parameters to verify what you actually catch and what still walks right past your rules.

Graft, as a Detection-as-Code tool, implements a best-effort approach to coverage analysis, with clear boundaries. Honestly, no single tool today gives you a complete coverage assessment out of the box as a quick win. The closest practical path is pairing an AI agent with a well-designed skill and deterministic, structured data underneath it. Even then, it starts with the team’s discipline to maintain clean rule-to-technique mappings and actually consume that data. Start small, stay consistent, and use AI where it helps — that is the approach that holds up in practice.

Reuse

Citation

BibTeX citation:
@online{lopes2026,
  author = {Lopes, Joe},
  title = {MITRE {Coverage} {Beyond} the {Green} {Matrix}},
  date = {2026-10-21},
  url = {https://lopes.id/log/mitre-coverage-beyond-the-matrix/},
  langid = {en}
}
For attribution, please cite this work as:
Lopes, Joe. 2026. “MITRE Coverage Beyond the Green Matrix.” October 21. https://lopes.id/log/mitre-coverage-beyond-the-matrix/.