Install any skill in seconds. Free to start, no credit card required.
Get Started Free →MITRE ATT&CK is a globally-accessible knowledge base of adversary tactics, techniques, and procedures (TTPs) based on real-world observations. This skill covers systematically mapping threat actor beh
.claude/skills/analyzing-threat-actor-ttps-with-mitre-attack/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-04 | ✗→✓ | ▲ Improved | — | — |
| case-05 | ✗→✓ | ▲ Improved | — | — |
| case-06 | ✗→✓ | ▲ Improved | — | — |
| case-02 | ✗→✓ | ▲ Improved | — | — |
| case-14 | ✓→✓ | = Same ✓ | — | — |
MITRE ATT&CK is a globally-accessible knowledge base of adversary tactics, techniques, and procedures (TTPs) based on real-world observations. This skill covers systematically mapping threat actor behavior to the ATT&CK framework, building technique coverage heatmaps using the ATT&CK Navigator, identifying detection gaps, and producing actionable intelligence reports that link observed IOCs to specific adversary techniques across the Enterprise, Mobile, and ICS matrices.
mitreattack-python, attackcti, stix2 librariesThe ATT&CK Enterprise matrix organizes adversary behavior into 14 Tactics (the "why") containing Techniques (the "how") and Sub-techniques (specific implementations). Each technique has associated data sources, detections, mitigations, and real-world procedure examples from observed threat groups.
ATT&CK catalogs over 140 threat groups (e.g., APT28, APT29, Lazarus Group, FIN7) with documented technique usage. Each group profile includes aliases, targeted sectors, associated campaigns, software used, and technique mappings with procedure-level detail.
The ATT&CK Navigator is a web-based tool for creating custom ATT&CK matrix visualizations. Analysts create layers (JSON files) that annotate techniques with scores, colors, comments, and metadata to visualize threat actor coverage, detection capabilities, or risk assessments.
pythonfrom attackcti import attack_client import json # Initialize ATT&CK client (queries MITRE TAXII server) lift = attack_client() # Get all Enterprise techniques enterprise_techniques = lift.get_enterprise_techniques() print(f"Total Enterprise techniques: {len(enterprise_techniques)}") # Get all threat groups groups = lift.get_groups() print(f"Total threat groups: {len(groups)}") # Get specific group by name apt29 = [g for g in groups if 'APT29' in g.get('name', '')] if apt29: group = apt29[0] print(f"Group: {group['name']}") print(f"Aliases: {group.get('aliases', [])}") print(f"Description: {group.get('description', '')[:200]}")
pythonfrom attackcti import attack_client lift = attack_client() # Get techniques used by APT29 apt29_techniques = lift.get_techniques_used_by_group("G0016") # APT29 group ID technique_map = {} for entry in apt29_techniques: tech_id = entry.get("external_references", [{}])[0].get("external_id", "") tech_name = entry.get("name", "") description = entry.get("description", "") tactic_refs = [ phase.get("phase_name", "") for phase in entry.get("kill_chain_phases", []) ] technique_map[tech_id] = { "name": tech_name, "tactics": tactic_refs, "description": description[:300], } print(f"\nAPT29 uses {len(technique_map)} techniques:") for tid, info in sorted(technique_map.items()): print(f" {tid}: {info['name']} [{', '.join(info['tactics'])}]")
pythonimport json def create_navigator_layer(group_name, technique_map, description=""): """Generate ATT&CK Navigator layer JSON for a threat group.""" techniques_list = [] for tech_id, info in technique_map.items(): techniques_list.append({ "techniqueID": tech_id, "tactic": info["tactics"][0] if info["tactics"] else "", "color": "#ff6666", # Red for observed techniques "comment": info["description"][:200], "enabled": True, "score": 100, "metadata": [ {"name": "group", "value": group_name}, ], }) layer = { "name": f"{group_name} TTP Coverage", "versions": { "attack": "16.1", "navigator": "5.1.0", "layer": "4.5", }, "domain": "enterprise-attack", "description": description or f"Techniques attributed to {group_name}", "filters": {"platforms": ["Windows", "Linux", "macOS", "Cloud"]}, "sorting": 0, "layout": { "layout": "side", "aggregateFunction": "average", "showID": True, "showName": True, "showAggregateScores": False, "countUnscored": False, }, "hideDisabled": False, "techniques": techniques_list, "gradient": { "colors": ["#ffffff", "#ff6666"], "minValue": 0, "maxValue": 100, }, "legendItems": [ {"label": "Observed technique", "color": "#ff6666"}, {"label": "Not observed", "color": "#ffffff"}, ], "showTacticRowBackground": True, "tacticRowBackground": "#dddddd", "selectTechniquesAcrossTactics": True, "selectSubtechniquesWithParent": False, "selectVisibleTechniques": False, } return layer # Generate and save layer layer = create_navigator_layer("APT29", technique_map, "APT29 (Cozy Bear) TTP analysis") with open("apt29_navigator_layer.json", "w") as f: json.dump(layer, f, indent=2) print("[+] Navigator layer saved to apt29_navigator_layer.json")
pythonfrom attackcti import attack_client lift = attack_client() # Get all techniques with data sources all_techniques = lift.get_enterprise_techniques() # Build data source coverage map data_source_coverage = {} for tech in all_techniques: tech_id = tech.get("external_references", [{}])[0].get("external_id", "") data_sources = tech.get("x_mitre_data_sources", []) for ds in data_sources: if ds not in data_source_coverage: data_source_coverage[ds] = [] data_source_coverage[ds].append(tech_id) # Compare threat actor techniques against available detections detected_techniques = {"T1059", "T1071", "T1566"} # Example: techniques you can detect actor_techniques = set(technique_map.keys()) covered = actor_techniques.intersection(detected_techniques) gaps = actor_techniques - detected_techniques print(f"\n=== Detection Gap Analysis for APT29 ===") print(f"Actor techniques: {len(actor_techniques)}") print(f"Detected: {len(covered)} ({len(covered)/len(actor_techniques)*100:.0f}%)") print(f"Gaps: {len(gaps)} ({len(gaps)/len(actor_techniques)*100:.0f}%)") print(f"\nUndetected techniques:") for tech_id in sorted(gaps): if tech_id in technique_map: print(f" {tech_id}: {technique_map[tech_id]['name']}")
pythonfrom attackcti import attack_client lift = attack_client() # Compare techniques across multiple groups groups_to_compare = { "G0016": "APT29", "G0007": "APT28", "G0032": "Lazarus Group", } group_techniques = {} for gid, gname in groups_to_compare.items(): techs = lift.get_techniques_used_by_group(gid) tech_ids = set() for t in techs: tid = t.get("external_references", [{}])[0].get("external_id", "") if tid: tech_ids.add(tid) group_techniques[gname] = tech_ids # Find common and unique techniques all_groups = list(group_techniques.keys()) common_to_all = set.intersection(*group_techniques.values()) print(f"\nTechniques common to all {len(all_groups)} groups: {len(common_to_all)}") for tid in sorted(common_to_all): print(f" {tid}") for gname, techs in group_techniques.items(): unique = techs - set.union(*[t for n, t in group_techniques.items() if n != gname]) print(f"\nUnique to {gname}: {len(unique)} techniques")
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-04 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-05 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-08 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-06 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-07 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-17 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-14 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-16 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-15 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-09 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-11 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-19 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-01 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-13 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-21 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-10 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-22 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-03 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-20 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-02 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-12 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-18 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +18 percentage points is the difference between those two pass rates over the 22 comparable cases.
The per-case answers from this run were removed by the retention sweep, so the case table below shows the verdicts without the text either arm produced. The counts above were recorded at the time and are unaffected. Answers are now kept for 180 days.
Other measured skills in the registry, with their headline benchmark lift.