threat intelligence

Analyzing Threat Actor TTPs with MITRE Navigator

Map advanced persistent threat (APT) group tactics, techniques, and procedures (TTPs) to the MITRE ATT&CK framework using the ATT&CK Navigator and attackcti Python library. The analyst queries STIX/TAXII data for group-technique associations, generates Navigator layer files for visualization, and compares defensive coverage against adversary profiles. Activates for requests involving APT TTP mapping, ATT&CK Navigator layers, threat actor profiling, or MITRE technique coverage analysis.

aptattackctimitre-attacknavigatorstixthreat-intelligencettp-mapping
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

Overview

The MITRE ATT&CK Navigator is a web application for annotating and visualizing ATT&CK matrices. Combined with the attackcti Python library (which queries ATT&CK STIX data via TAXII), analysts can programmatically generate Navigator layer files mapping specific threat group TTPs, compare multiple groups, and assess detection coverage gaps against known adversaries.

When to Use

  • When investigating security incidents that require analyzing threat actor ttps with mitre navigator
  • When building detection rules or threat hunting queries for this domain
  • When SOC analysts need structured procedures for this analysis type
  • When validating security monitoring coverage for related attack techniques

Prerequisites

  • Python 3.8+ with attackcti and stix2 libraries installed
  • MITRE ATT&CK Navigator (web UI or local instance)
  • Understanding of STIX 2.1 objects and relationships

Steps

  1. Query ATT&CK STIX data for target threat group using attackcti
  2. Extract techniques associated with the group via STIX relationships
  3. Generate ATT&CK Navigator layer JSON with technique annotations
  4. Overlay detection coverage to identify gaps
  5. Export layer for team review and defensive planning

Expected Output

{
  "name": "APT29 TTPs",
  "domain": "enterprise-attack",
  "techniques": [
    {"techniqueID": "T1566.001", "score": 1, "comment": "Spearphishing Attachment"},
    {"techniqueID": "T1059.001", "score": 1, "comment": "PowerShell"}
  ]
}
Source materials

References and resources

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

References 1

api-reference.md3.5 KB

Analyzing Threat Actor TTPs with MITRE Navigator — API Reference

attackcti Python Library

Method Description
attack_client() Initialize STIX/TAXII client for ATT&CK data
client.get_groups() Retrieve all threat groups from ATT&CK
client.get_techniques() Retrieve all techniques from ATT&CK
client.get_techniques_used_by_group(group) Get techniques linked to a specific group
client.get_software() Retrieve all software/tools from ATT&CK
client.get_software_used_by_group(group) Get software used by a specific group
client.get_mitigations() Retrieve all mitigations from ATT&CK
client.get_data_sources() Retrieve all data sources from ATT&CK

STIX 2.1 Group Object Fields

Field Description
id STIX object ID (e.g., intrusion-set--abc123)
name Group name (e.g., APT29)
aliases Alternative names for the group
description Group description and background
external_references List of references including ATT&CK ID
created Object creation timestamp
modified Last modification timestamp

STIX 2.1 Technique Object Fields

Field Description
name Technique name (e.g., Spearphishing Attachment)
external_references[].external_id ATT&CK technique ID (e.g., T1566.001)
x_mitre_platforms Target platforms (Windows, Linux, macOS)
kill_chain_phases Associated tactics in the kill chain
x_mitre_detection Detection guidance for the technique
x_mitre_is_subtechnique Whether this is a sub-technique

ATT&CK Navigator Layer JSON Schema

Field Type Description
name string Layer display name
versions.attack string ATT&CK version (e.g., "15")
versions.navigator string Navigator version (e.g., "5.0")
versions.layer string Layer format version (e.g., "4.5")
domain string enterprise-attack, mobile-attack, or ics-attack
techniques[].techniqueID string ATT&CK technique ID
techniques[].score integer Numeric score for coloring (0-100)
techniques[].color string Hex color override (e.g., #ff6666)
techniques[].comment string Annotation text for the technique
techniques[].enabled boolean Whether technique cell is enabled
gradient.colors array Color gradient from min to max score
gradient.minValue integer Minimum score value
gradient.maxValue integer Maximum score value
filters.platforms array Platforms to display in the matrix
legendItems[].label string Legend entry label
legendItems[].color string Legend entry color

CLI Usage

# List all ATT&CK threat groups
python agent.py --list-groups
 
# Analyze a specific group
python agent.py --group "APT29"
 
# Generate Navigator layer file
python agent.py --group "APT29" --layer-output apt29_layer.json
 
# Compare multiple groups
python agent.py --compare "APT29" "APT28" "Lazarus Group"
 
# Save full report as JSON
python agent.py --group "APT29" --layer-output apt29.json --output report.json

External References

Scripts 1

agent.py7.4 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""MITRE ATT&CK Navigator layer generation and threat actor TTP mapping agent."""

import json
import sys
import argparse
from datetime import datetime

try:
    from attackcti import attack_client
except ImportError:
    print("Install: pip install attackcti")
    sys.exit(1)


def get_attack_client():
    """Initialize ATT&CK STIX/TAXII client."""
    return attack_client()


def list_threat_groups(client):
    """List all threat groups in ATT&CK."""
    groups = client.get_groups()
    results = []
    for g in groups:
        aliases = g.get("aliases", [])
        results.append({
            "name": g.get("name", ""),
            "id": g.get("external_references", [{}])[0].get("external_id", "")
                  if g.get("external_references") else "",
            "aliases": aliases,
            "description": g.get("description", "")[:200],
        })
    return sorted(results, key=lambda x: x["name"])


def get_group_techniques(client, group_name):
    """Get all techniques used by a specific threat group."""
    groups = client.get_groups()
    target_group = None
    for g in groups:
        if g.get("name", "").lower() == group_name.lower():
            target_group = g
            break
        aliases = [a.lower() for a in g.get("aliases", [])]
        if group_name.lower() in aliases:
            target_group = g
            break

    if not target_group:
        return {"error": f"Group '{group_name}' not found"}

    group_stix_id = target_group["id"]
    techniques = client.get_techniques_used_by_group(target_group)

    results = []
    for tech in techniques:
        ext_refs = tech.get("external_references", [])
        tech_id = ""
        url = ""
        for ref in ext_refs:
            if ref.get("source_name") == "mitre-attack":
                tech_id = ref.get("external_id", "")
                url = ref.get("url", "")
                break
        results.append({
            "technique_id": tech_id,
            "name": tech.get("name", ""),
            "description": tech.get("description", "")[:150],
            "url": url,
            "platforms": tech.get("x_mitre_platforms", []),
        })

    return {
        "group_name": target_group.get("name", ""),
        "group_id": target_group.get("external_references", [{}])[0].get("external_id", ""),
        "technique_count": len(results),
        "techniques": results,
    }


def generate_navigator_layer(group_data, color="#ff6666"):
    """Generate ATT&CK Navigator layer JSON from group technique data."""
    techniques = []
    for tech in group_data.get("techniques", []):
        tid = tech.get("technique_id", "")
        if not tid:
            continue
        techniques.append({
            "techniqueID": tid,
            "score": 1,
            "color": color,
            "comment": tech.get("name", ""),
            "enabled": True,
        })

    layer = {
        "name": f"{group_data.get('group_name', 'Unknown')} TTPs",
        "versions": {
            "attack": "15",
            "navigator": "5.0",
            "layer": "4.5",
        },
        "domain": "enterprise-attack",
        "description": f"Techniques used by {group_data.get('group_name', '')} "
                        f"({group_data.get('group_id', '')})",
        "filters": {"platforms": ["Windows", "Linux", "macOS", "Cloud"]},
        "sorting": 0,
        "layout": {"layout": "side", "showID": True, "showName": True},
        "hideDisabled": False,
        "techniques": techniques,
        "gradient": {
            "colors": ["#ffffff", color],
            "minValue": 0,
            "maxValue": 1,
        },
        "legendItems": [
            {"label": f"{group_data.get('group_name', '')} techniques", "color": color},
        ],
        "metadata": [],
        "showTacticRowBackground": True,
        "tacticRowBackground": "#dddddd",
    }
    return layer


def compare_groups(client, group_names):
    """Compare techniques across multiple threat groups."""
    all_techniques = {}
    group_techs = {}
    for name in group_names:
        data = get_group_techniques(client, name)
        if "error" in data:
            continue
        techs = {t["technique_id"] for t in data.get("techniques", [])}
        group_techs[data.get("group_name", name)] = techs
        for t in data.get("techniques", []):
            all_techniques[t["technique_id"]] = t["name"]

    shared = set.intersection(*group_techs.values()) if group_techs else set()
    unique_per_group = {}
    for name, techs in group_techs.items():
        unique_per_group[name] = techs - set.union(*(v for k, v in group_techs.items() if k != name))

    return {
        "groups_compared": list(group_techs.keys()),
        "total_unique_techniques": len(set.union(*group_techs.values())) if group_techs else 0,
        "shared_techniques": [{"id": t, "name": all_techniques.get(t, "")} for t in shared],
        "shared_count": len(shared),
        "unique_per_group": {k: len(v) for k, v in unique_per_group.items()},
    }


def run_audit(args):
    """Execute threat actor TTP mapping audit."""
    print(f"\n{'='*60}")
    print(f"  MITRE ATT&CK THREAT ACTOR TTP ANALYSIS")
    print(f"  Generated: {datetime.utcnow().isoformat()} UTC")
    print(f"{'='*60}\n")

    client = get_attack_client()
    report = {}

    if args.list_groups:
        groups = list_threat_groups(client)
        report["groups"] = groups
        print(f"--- THREAT GROUPS ({len(groups)}) ---")
        for g in groups[:30]:
            print(f"  {g['id']}: {g['name']}")

    if args.group:
        data = get_group_techniques(client, args.group)
        report["group_techniques"] = data
        print(f"--- {data.get('group_name','')} ({data.get('group_id','')}) ---")
        print(f"  Techniques: {data.get('technique_count', 0)}")
        for t in data.get("techniques", [])[:20]:
            print(f"  {t['technique_id']}: {t['name']}")

        if args.layer_output:
            layer = generate_navigator_layer(data)
            with open(args.layer_output, "w") as f:
                json.dump(layer, f, indent=2)
            report["layer_file"] = args.layer_output
            print(f"\n  Navigator layer saved to {args.layer_output}")

    if args.compare:
        comparison = compare_groups(client, args.compare)
        report["comparison"] = comparison
        print(f"\n--- GROUP COMPARISON ---")
        print(f"  Groups: {comparison['groups_compared']}")
        print(f"  Total unique techniques: {comparison['total_unique_techniques']}")
        print(f"  Shared: {comparison['shared_count']}")
        for t in comparison["shared_techniques"][:10]:
            print(f"    {t['id']}: {t['name']}")

    return report


def main():
    parser = argparse.ArgumentParser(description="MITRE ATT&CK TTP Mapping Agent")
    parser.add_argument("--group", help="Threat group name to analyze (e.g., APT29)")
    parser.add_argument("--list-groups", action="store_true", help="List all ATT&CK groups")
    parser.add_argument("--compare", nargs="+", help="Compare multiple groups")
    parser.add_argument("--layer-output", help="Save Navigator layer JSON to file")
    parser.add_argument("--output", help="Save report to JSON file")
    args = parser.parse_args()

    report = run_audit(args)
    if args.output:
        with open(args.output, "w") as f:
            json.dump(report, f, indent=2, default=str)
        print(f"\n[+] Report saved to {args.output}")


if __name__ == "__main__":
    main()
Keep exploring