threat intelligence

Mapping MITRE ATT&CK Techniques

Maps observed adversary behaviors, security alerts, and detection rules to MITRE ATT&CK techniques and sub-techniques to quantify detection coverage and guide control prioritization. Use when building an ATT&CK-based coverage heatmap, tagging SIEM alerts with technique IDs, aligning security controls to adversary playbooks, or reporting threat exposure to executives. Activates for requests involving ATT&CK Navigator, Sigma rules, MITRE D3FEND, or coverage gap analysis.

att&ck-navigatord3fenddetection-engineeringmitre-att&cknist-csfsigmattp
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

When to Use

Use this skill when:

  • Generating an ATT&CK coverage heatmap to show which techniques your detection stack addresses
  • Tagging existing SIEM use cases or Sigma rules with ATT&CK technique IDs for structured reporting
  • Aligning your security program roadmap to specific adversary groups known to target your sector

Do not use this skill for real-time incident triage — ATT&CK mapping is an analytical activity best performed post-detection or during threat hunting planning.

Prerequisites

Workflow

Step 1: Obtain Current ATT&CK Data

Download the latest ATT&CK STIX bundle for the relevant matrix (Enterprise, Mobile, ICS):

curl -o enterprise-attack.json \
  https://raw.githubusercontent.com/mitre/cti/master/enterprise-attack/enterprise-attack.json

Use the mitreattack-python library to query techniques programmatically:

from mitreattack.stix20 import MitreAttackData
 
mitre = MitreAttackData("enterprise-attack.json")
techniques = mitre.get_techniques(remove_revoked_deprecated=True)
for t in techniques[:5]:
    print(t["external_references"][0]["external_id"], t["name"])

Step 2: Map Existing Detections to Techniques

For each SIEM rule or Sigma file, assign ATT&CK technique IDs. Sigma rules support native ATT&CK tagging:

tags:
  - attack.execution
  - attack.t1059.001  # PowerShell
  - attack.t1059.003  # Windows Command Shell

Create a coverage matrix: list each technique ID and mark as: Detected (alert fires), Logged (data present but no alert), Blind (no data source).

Step 3: Prioritize Coverage Gaps Using Threat Intelligence

Cross-reference coverage gaps with adversary groups targeting your sector. Use ATT&CK Groups data:

groups = mitre.get_groups()
apt29 = mitre.get_object_by_attack_id("G0016", "groups")
apt29_techniques = mitre.get_techniques_used_by_group(apt29)
for t in apt29_techniques:
    print(t["object"]["external_references"][0]["external_id"])

Prioritize adding detection for techniques used by high-priority threat groups where your coverage is blind.

Step 4: Build Navigator Heatmap

Export coverage scores as ATT&CK Navigator JSON layer:

import json
 
layer = {
    "name": "SOC Detection Coverage Q1 2025",
    "versions": {"attack": "14", "navigator": "4.9", "layer": "4.5"},
    "domain": "enterprise-attack",
    "techniques": [
        {"techniqueID": "T1059.001", "score": 100, "comment": "Splunk rule: PS_Encoded_Command"},
        {"techniqueID": "T1071.001", "score": 50, "comment": "Logged only, no alert"},
        {"techniqueID": "T1055", "score": 0, "comment": "No coverage — blind spot"}
    ],
    "gradient": {"colors": ["#ff6666", "#ffe766", "#8ec843"], "minValue": 0, "maxValue": 100}
}
with open("coverage_layer.json", "w") as f:
    json.dump(layer, f)

Import layer into ATT&CK Navigator (https://mitre-attack.github.io/attack-navigator/) for visualization.

Step 5: Generate Executive Coverage Report

Summarize coverage by tactic category (Initial Access, Execution, Persistence, etc.) with counts and percentages. Provide a risk-ranked list of top 10 blind-spot techniques based on adversary group usage frequency. Recommend data source additions (e.g., "Enable PowerShell Script Block Logging to address 12 Execution sub-technique gaps").

Key Concepts

Term Definition
ATT&CK Technique Specific adversary method identified by T-number (e.g., T1059 = Command and Scripting Interpreter)
Sub-technique More granular variant of a technique (e.g., T1059.001 = PowerShell, T1059.003 = Windows Command Shell)
Tactic Adversary goal category in ATT&CK: Initial Access, Execution, Persistence, Privilege Escalation, Defense Evasion, Credential Access, Discovery, Lateral Movement, Collection, C&C, Exfiltration, Impact
Data Source ATT&CK v10+ component identifying telemetry required to detect a technique (e.g., Process Creation, Network Traffic)
Coverage Score Numeric (0–100) representing detection completeness for a technique: 0=blind, 50=logged only, 100=alerted
MITRE D3FEND Defensive countermeasure ontology complementing ATT&CK — maps defensive techniques to attack techniques they mitigate

Tools & Systems

  • ATT&CK Navigator: Browser-based heatmap visualization tool for layering coverage scores and annotations on the ATT&CK matrix
  • mitreattack-python: Official MITRE Python library for programmatic access to ATT&CK STIX data (techniques, groups, software, mitigations)
  • Atomic Red Team: MITRE-aligned test library providing atomic test cases to validate detection for each technique
  • Sigma: Detection rule format with ATT&CK tagging support; translatable to Splunk, Sentinel, QRadar, Elastic
  • ATT&CK Workbench: Self-hosted ATT&CK knowledge base for organizations maintaining custom technique extensions

Common Pitfalls

  • Over-claiming coverage: Logging a data source (e.g., process creation events) does not mean the associated technique is detected — a rule must actually fire on malicious patterns.
  • Mapping at tactic level only: Tagging a rule as "attack.execution" without a specific technique ID prevents granular gap analysis.
  • Ignoring sub-techniques: Many adversaries use specific sub-techniques. Coverage of T1059 (parent) doesn't imply coverage of T1059.005 (Visual Basic).
  • Static mapping without updates: ATT&CK releases major versions annually. Coverage maps go stale as techniques are added, revised, or deprecated.
  • Not mapping to adversary groups: Generic coverage maps don't distinguish between techniques used by APTs targeting your sector vs. commodity malware.
Source materials

References and resources

Everything below is rendered for inspection. Script files are read-only and never run.

References 1

api-reference.md2.6 KB

API Reference: Mapping MITRE ATT&CK Techniques

mitreattack-python Library

Method Description
MitreAttackData(stix_filepath=path) Load ATT&CK STIX 2.0 data bundle from file
get_techniques(remove_revoked_deprecated=False) Returns list[AttackPattern] STIX objects
get_groups(remove_revoked_deprecated=False) Returns list[IntrusionSet] STIX objects
get_techniques_used_by_group(group_stix_id) Returns list[dict] with t["object"] as AttackPattern
get_attack_id(stix_id=id) Resolve STIX ID to ATT&CK ID (e.g., T1059)
get_mitigations(remove_revoked_deprecated=False) Returns list[CourseOfAction]
get_software(remove_revoked_deprecated=False) Returns list[Malware or Tool]

ATT&CK Navigator API (Layer Format)

Field Type Description
techniques[].techniqueID string ATT&CK technique ID (e.g., T1059)
techniques[].score number Coverage score (0=gap, 1=detected)
techniques[].color string Hex color for heatmap visualization
domain string ATT&CK domain: enterprise-attack, mobile-attack, ics-attack

MITRE ATT&CK TAXII Server

Endpoint Description
cti-taxii.mitre.org/stix/collections/ List available STIX collections
cti-taxii.mitre.org/stix/collections/{id}/objects/ Download STIX objects

Sigma Rules (Detection Engineering)

Field Description
tags ATT&CK mapping (e.g., attack.t1059.001)
logsource.product Target log source (windows, linux, aws)
detection Search logic with conditions

Key Libraries

  • mitreattack-python (pip install mitreattack-python): Official MITRE ATT&CK Python library
  • stix2: Parse and create STIX 2.1 objects
  • taxii2-client: Download ATT&CK data from TAXII server
  • pySigma: Parse and convert Sigma detection rules

Configuration

Variable Description
ATTACK_STIX_PATH Path to local enterprise-attack.json STIX bundle
NAVIGATOR_URL ATT&CK Navigator instance URL

Data Sources

Source URL Description
ATT&CK STIX github.com/mitre/cti Official STIX bundles
ATT&CK Navigator github.com/mitre-attack/attack-navigator Layer visualization tool
Sigma Rules github.com/SigmaHQ/sigma Community detection rules

References

Scripts 1

agent.py7.6 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""
MITRE ATT&CK Technique Mapping Agent
Maps detection rules and security alerts to ATT&CK techniques using
the mitreattack-python library. Generates coverage heatmaps and identifies gaps.
"""

import json
import os
import sys
from datetime import datetime, timezone

from mitreattack.stix20 import MitreAttackData


def load_attack_data(stix_path: str = None) -> MitreAttackData:
    """Load MITRE ATT&CK STIX data bundle."""
    if stix_path and os.path.exists(stix_path):
        return MitreAttackData(stix_filepath=stix_path)
    return MitreAttackData(stix_filepath="enterprise-attack.json")


def get_all_techniques(attack_data: MitreAttackData) -> list[dict]:
    """Retrieve all Enterprise ATT&CK techniques with metadata.
    Returns list[AttackPattern] (STIX objects supporting dict-like access).
    """
    techniques = attack_data.get_techniques(remove_revoked_deprecated=True)
    result = []
    for tech in techniques:
        # Use get_attack_id() to resolve STIX ID -> ATT&CK ID (e.g. T1059)
        tech_id = attack_data.get_attack_id(stix_id=tech.id) or ""

        platforms = tech.get("x_mitre_platforms", [])
        tactics = []
        for phase in tech.get("kill_chain_phases", []):
            if phase.get("kill_chain_name") == "mitre-attack":
                tactics.append(phase.get("phase_name", ""))

        result.append({
            "id": tech_id,
            "name": tech.name,
            "tactics": tactics,
            "platforms": platforms,
            "is_subtechnique": tech.get("x_mitre_is_subtechnique", False),
        })

    return sorted(result, key=lambda x: x["id"])


def get_techniques_by_group(attack_data: MitreAttackData, group_name: str) -> list[str]:
    """Get techniques used by a specific threat group.
    Groups are IntrusionSet STIX objects; techniques retrieved via relationship query.
    """
    groups = attack_data.get_groups(remove_revoked_deprecated=True)
    target_group = None
    for group in groups:
        if group.name.lower() == group_name.lower():
            target_group = group
            break
        for alias in group.get("aliases", []):
            if alias.lower() == group_name.lower():
                target_group = group
                break

    if not target_group:
        return []

    # get_techniques_used_by_group returns list of RelationshipEntry dicts
    # Each entry has t["object"] = AttackPattern STIX object
    techniques = attack_data.get_techniques_used_by_group(target_group.id)
    tech_ids = []
    for t in techniques:
        technique = t["object"]
        attack_id = attack_data.get_attack_id(stix_id=technique.id)
        if attack_id:
            tech_ids.append(attack_id)

    return sorted(tech_ids)


def load_detection_rules(rules_file: str) -> list[dict]:
    """Load detection rules with ATT&CK technique tags."""
    if os.path.exists(rules_file):
        with open(rules_file, "r") as f:
            return json.load(f)
    return []


def calculate_coverage(all_techniques: list[dict], detected_technique_ids: set) -> dict:
    """Calculate ATT&CK coverage statistics by tactic."""
    tactic_coverage = {}

    for tech in all_techniques:
        if tech["is_subtechnique"]:
            continue
        for tactic in tech["tactics"]:
            if tactic not in tactic_coverage:
                tactic_coverage[tactic] = {"total": 0, "covered": 0, "uncovered_techniques": []}
            tactic_coverage[tactic]["total"] += 1
            if tech["id"] in detected_technique_ids:
                tactic_coverage[tactic]["covered"] += 1
            else:
                tactic_coverage[tactic]["uncovered_techniques"].append(tech["id"])

    for tactic, data in tactic_coverage.items():
        data["coverage_pct"] = round(data["covered"] / max(data["total"], 1) * 100, 1)

    total_techniques = len([t for t in all_techniques if not t["is_subtechnique"]])
    covered = len(detected_technique_ids & {t["id"] for t in all_techniques if not t["is_subtechnique"]})

    return {
        "overall_coverage_pct": round(covered / max(total_techniques, 1) * 100, 1),
        "total_techniques": total_techniques,
        "covered_techniques": covered,
        "by_tactic": tactic_coverage,
    }


def generate_navigator_layer(techniques: list[dict], detected_ids: set, layer_name: str) -> dict:
    """Generate ATT&CK Navigator JSON layer for visualization."""
    tech_entries = []
    for tech in techniques:
        score = 1 if tech["id"] in detected_ids else 0
        color = "#31a354" if score == 1 else ""
        tech_entries.append({
            "techniqueID": tech["id"],
            "score": score,
            "color": color,
            "enabled": True,
        })

    return {
        "name": layer_name,
        "versions": {"attack": "14", "navigator": "4.9.1", "layer": "4.5"},
        "domain": "enterprise-attack",
        "description": f"Detection coverage layer generated {datetime.now(timezone.utc).strftime('%Y-%m-%d')}",
        "gradient": {"colors": ["#ff6666", "#31a354"], "minValue": 0, "maxValue": 1},
        "techniques": tech_entries,
    }


def identify_priority_gaps(coverage: dict, group_techniques: list[str]) -> list[dict]:
    """Identify high-priority coverage gaps based on threat group activity."""
    gaps = []
    all_uncovered = set()
    for tactic, data in coverage["by_tactic"].items():
        all_uncovered.update(data["uncovered_techniques"])

    for tech_id in group_techniques:
        if tech_id in all_uncovered:
            gaps.append({"technique_id": tech_id, "reason": "Used by target threat group, no detection"})

    return gaps


def generate_report(coverage: dict, gaps: list, group_name: str) -> str:
    """Generate ATT&CK mapping report."""
    lines = [
        "MITRE ATT&CK DETECTION COVERAGE REPORT",
        "=" * 50,
        f"Report Date: {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M UTC')}",
        "",
        f"Overall Coverage: {coverage['overall_coverage_pct']}%",
        f"  Techniques Covered: {coverage['covered_techniques']}/{coverage['total_techniques']}",
        "",
        "COVERAGE BY TACTIC:",
    ]
    for tactic, data in sorted(coverage["by_tactic"].items()):
        bar = "#" * int(data["coverage_pct"] / 5) + "." * (20 - int(data["coverage_pct"] / 5))
        lines.append(f"  {tactic:35s} [{bar}] {data['coverage_pct']}%")

    if gaps:
        lines.extend(["", f"PRIORITY GAPS (Threat Group: {group_name}):", "-" * 40])
        for gap in gaps[:15]:
            lines.append(f"  [GAP] {gap['technique_id']} - {gap['reason']}")

    return "\n".join(lines)


if __name__ == "__main__":
    stix_path = sys.argv[1] if len(sys.argv) > 1 else "enterprise-attack.json"
    rules_file = sys.argv[2] if len(sys.argv) > 2 else "detection_rules.json"
    group_name = sys.argv[3] if len(sys.argv) > 3 else "APT29"

    print("[*] Loading MITRE ATT&CK data...")
    attack_data = load_attack_data(stix_path)
    all_techniques = get_all_techniques(attack_data)
    print(f"[*] Loaded {len(all_techniques)} techniques")

    rules = load_detection_rules(rules_file)
    detected_ids = set()
    for rule in rules:
        detected_ids.update(rule.get("attack_ids", []))

    coverage = calculate_coverage(all_techniques, detected_ids)
    group_techs = get_techniques_by_group(attack_data, group_name)
    gaps = identify_priority_gaps(coverage, group_techs)

    report = generate_report(coverage, gaps, group_name)
    print(report)

    layer = generate_navigator_layer(all_techniques, detected_ids, "Detection Coverage")
    with open("attack_navigator_layer.json", "w") as f:
        json.dump(layer, f, indent=2)
    print(f"\n[*] Navigator layer saved to attack_navigator_layer.json")
Keep exploring