Skip to content

MITRE ATT&CK Coverage

Sigma rules carry ATT&CK tags, and Bifract stores them verbatim on the alert:

tags:
    - attack.persistence
    - attack.privilege-escalation
    - attack.t1543.003
    - attack.t1068

Alerts > Coverage reads those tags back against an embedded copy of the ATT&CK Enterprise matrix and renders the tactic-column grid, heat-mapped by how many of your rules cover each technique. The page is scoped to the fractal or prism you are in.

Reading the map

Each column is a tactic in kill-chain order and each cell is a technique. By default the color says what state the technique is in, which is the question the page exists to answer:

Cell Meaning
Filled purple, badged with a count At least one enabled rule maps here. The fill deepens with the rule count
Dashed purple outline Every rule mapped here is switched off. Covered on paper, detecting nothing
Amber, badged with a count No rule here, but a synced feed carries that many rules that were never imported. These are the gaps you can close today
Flat, unbadged No rule, and nothing waiting in a feed. This one needs a new detection

Switch Color to rule count or highest severity when you are tuning depth rather than looking for holes.

The header carries one number, Coverage, and a chip for each thing outstanding: gaps a feed could close, techniques covered only by disabled rules, rules carrying no ATT&CK tag, and broken tags. A chip is rendered only when its count is not zero, and the first two are filters: click one and the grid narrows to the cells it counted.

Each column header shows that tactic's coverage over a two-part meter: purple for what is covered, amber for the share a feed could close on top of it.

Cells with a chevron have sub-techniques; expand them individually or turn on Sub-techniques to expand every group.

How coverage is counted

Two numbers, and the difference between them matters.

Coverage (the headline, and the number in each column header) counts detectable units: every sub-technique, plus every technique that has none. Nothing is inherited. A rule tagged attack.t1547 covers the parent but zero of its 14 autostart sub-techniques, because the tag does not say which mechanism the rule catches.

Techniques touched counts top-level techniques with at least one rule mapped to them or to any of their sub-techniques. A technique with 14 sub-techniques and 1 covered scores a full point here. It lives in the tooltip on Coverage and on each column header rather than on the page, because it is the flattering number of the two and does not belong in a glance.

The gap between them is usually large:

Tactic Coverage Techniques touched
Privilege Escalation 36/85 (42%) 12/13 (92%)
Persistence 42/99 (42%) 17/22 (77%)
Credential Access 26/58 (45%) 14/17 (82%)

Reading the right-hand column as your coverage overstates it by roughly a factor of two. Use "techniques touched" for breadth, "Coverage" for depth.

Three further counting rules:

  • A rule tagged with a sub-technique (attack.t1543.003) counts as direct coverage of that sub-technique and inherited coverage of its parent. The drawer and tooltip report the two separately.
  • A rule tagged with only a tactic (attack.persistence, no technique) is not coverage of anything on the grid. It is counted under "rules mapped" as unmapped, because it does not say which technique it detects.
  • A rule tagged with a retired technique ID resolves to its replacement where ATT&CK defines one (attack.t1086 counts toward T1059.001). An ID with no replacement, usually a typo or a technique MITRE removed outright, is invisible to the map; they are counted by the broken tags chip in the header so you can fix the rule.

Deprecated techniques are excluded from every denominator.

Controls

Control Effect
Search Dims non-matching cells and expands any sub-technique group holding a match. Matches technique IDs too, which are not printed on the cells
Color Status, rule count or highest severity
Filters Show (all / gaps only / covered only), severity, platform, source and enabled-only, folded into one control with a badge showing how many are active. The platform filter narrows the technique universe too, so the percentage stays honest
Sub-techniques Expands every sub-technique group at once
Export layer Downloads your coverage as an ATT&CK Navigator layer (see below)

Export layer

Layer is ATT&CK Navigator's term for a coverage overlay: a JSON file of {techniqueID, score, comment} entries that MITRE's viewer paints onto the matrix. It is the interchange format the ATT&CK ecosystem speaks, so exporting one lets you:

  • Open Bifract's coverage in MITRE's Navigator and hand the file to people who have no Bifract access
  • Diff it against another source. Navigator subtracts one layer from another, so you can overlay your EDR vendor's claimed coverage, a red team's layer, or a threat group's technique set (published on attack.mitre.org) and see where you are exposed
  • Keep a dated snapshot to show coverage growth over time

The export honours the active filters, so "Windows only" or "critical severity only" can be its own layer. Scores are rule counts, and each entry's comment records the direct/inherited split.

Clicking any cell opens a drawer with the technique's tactics, the rules covering it (click one to open it in the alert editor), its platforms, the telemetry MITRE expects it to be detectable in, and a link to attack.mitre.org.

Closing a gap

An amber cell means a feed you have configured carries rules for that technique that were never imported. Open it and the drawer lists them, each with the reason it is not running:

  • below the feed severity threshold: the rule's level is under the feed's Min Level
  • below the feed maturity threshold: the rule's status is under the feed's Min Status
  • cannot be translated to BQL: the rule parsed but Bifract cannot express its detection logic
  • failed to import: the translation produced a query Bifract's own parser rejected

The first two are a threshold you chose: lower the feed's Min Level or Min Status to pull those rules in. The last two are translator work, and their counts are the clearest signal of which Sigma constructs to support next. Nothing is imported behind your back.

For a technique with nothing waiting, Write a detection opens the alert editor with the name, the attack.tNNNN label and MITRE's expected telemetry already filled in, so the rule that closes the gap is tagged for the map without you having to remember.

The rule catalog

The amber cells are backed by feed_rule_catalog, which records every rule a feed's repository offers, imported or not. It is populated on every feed sync, so a freshly upgraded install shows no candidates until its feeds sync again. Trigger a sync from Alerts > Feeds to populate it immediately.

Catalog metadata is read before translation is attempted, which is the point: a rule Bifract cannot translate still has ATT&CK tags, and those are exactly the gaps worth knowing about.

Updating the ATT&CK matrix

The Enterprise matrix is embedded in the binary (gzipped, ~17 KB) rather than fetched at runtime, so air-gapped installs work and startup has no dependency on GitHub. Regenerate it when MITRE publishes a new version:

go run ./cmd/bifract-attackgen -out pkg/attack/data/enterprise-attack.json.gz

That downloads MITRE's STIX bundle, slims it, validates the result, and writes the embedded file; pass -in enterprise-attack.json to use a local copy. Commit the regenerated file. go test ./pkg/attack/... verifies that it loads, that every tactic column is populated in kill-chain order, and that no sub-technique has a dangling parent.

ATT&CK renames tactics between versions (v19 renamed Defense Evasion to Stealth) while rule sets keep emitting the old slug for years. Bifract resolves both, so attack.defense-evasion keeps working regardless of the version embedded.