Files
Anthropic-Cybersecurity-Skills/skills/implementing-mitre-attack-coverage-mapping/scripts/process.py
T

199 lines
8.8 KiB
Python

#!/usr/bin/env python3
"""
MITRE ATT&CK Coverage Mapping Tool
Builds and analyzes detection coverage maps against the
MITRE ATT&CK framework for SOC detection gap analysis.
"""
import json
from datetime import datetime
ATTACK_TACTICS = {
"TA0043": "Reconnaissance",
"TA0042": "Resource Development",
"TA0001": "Initial Access",
"TA0002": "Execution",
"TA0003": "Persistence",
"TA0004": "Privilege Escalation",
"TA0005": "Defense Evasion",
"TA0006": "Credential Access",
"TA0007": "Discovery",
"TA0008": "Lateral Movement",
"TA0009": "Collection",
"TA0011": "Command and Control",
"TA0010": "Exfiltration",
"TA0040": "Impact",
}
ENTERPRISE_TECHNIQUES = {
"T1110": {"name": "Brute Force", "tactic": "TA0006", "subtechniques": 4},
"T1059": {"name": "Command and Scripting Interpreter", "tactic": "TA0002", "subtechniques": 9},
"T1078": {"name": "Valid Accounts", "tactic": "TA0005", "subtechniques": 4},
"T1055": {"name": "Process Injection", "tactic": "TA0005", "subtechniques": 15},
"T1021": {"name": "Remote Services", "tactic": "TA0008", "subtechniques": 7},
"T1053": {"name": "Scheduled Task/Job", "tactic": "TA0003", "subtechniques": 6},
"T1566": {"name": "Phishing", "tactic": "TA0001", "subtechniques": 4},
"T1003": {"name": "OS Credential Dumping", "tactic": "TA0006", "subtechniques": 8},
"T1071": {"name": "Application Layer Protocol", "tactic": "TA0011", "subtechniques": 4},
"T1048": {"name": "Exfiltration Over Alternative Protocol", "tactic": "TA0010", "subtechniques": 3},
"T1105": {"name": "Ingress Tool Transfer", "tactic": "TA0011", "subtechniques": 0},
"T1547": {"name": "Boot or Logon Autostart Execution", "tactic": "TA0003", "subtechniques": 15},
"T1036": {"name": "Masquerading", "tactic": "TA0005", "subtechniques": 9},
"T1218": {"name": "System Binary Proxy Execution", "tactic": "TA0005", "subtechniques": 14},
"T1027": {"name": "Obfuscated Files or Information", "tactic": "TA0005", "subtechniques": 12},
"T1486": {"name": "Data Encrypted for Impact", "tactic": "TA0040", "subtechniques": 0},
"T1098": {"name": "Account Manipulation", "tactic": "TA0003", "subtechniques": 6},
"T1070": {"name": "Indicator Removal", "tactic": "TA0005", "subtechniques": 9},
"T1543": {"name": "Create or Modify System Process", "tactic": "TA0003", "subtechniques": 4},
"T1136": {"name": "Create Account", "tactic": "TA0003", "subtechniques": 3},
}
class DetectionRule:
"""Represents a SIEM detection rule with MITRE mapping."""
def __init__(self, name: str, techniques: list, score: int,
data_sources: list, validated: bool = False):
self.name = name
self.techniques = techniques
self.score = score
self.data_sources = data_sources
self.validated = validated
class CoverageMap:
"""MITRE ATT&CK coverage map for detection gap analysis."""
def __init__(self, organization: str):
self.organization = organization
self.rules = []
self.technique_scores = {}
self.generated = datetime.utcnow().isoformat()
def add_rule(self, rule: DetectionRule):
self.rules.append(rule)
for tech_id in rule.techniques:
if tech_id not in self.technique_scores:
self.technique_scores[tech_id] = {"rules": [], "max_score": 0}
self.technique_scores[tech_id]["rules"].append(rule.name)
self.technique_scores[tech_id]["max_score"] = max(
self.technique_scores[tech_id]["max_score"], rule.score
)
def get_coverage_summary(self) -> dict:
total_techniques = len(ENTERPRISE_TECHNIQUES)
covered = sum(1 for t in ENTERPRISE_TECHNIQUES if t in self.technique_scores and self.technique_scores[t]["max_score"] > 0)
no_coverage = total_techniques - covered
scores = [self.technique_scores.get(t, {}).get("max_score", 0) for t in ENTERPRISE_TECHNIQUES]
avg_score = round(sum(scores) / max(1, len(scores)), 1)
return {
"organization": self.organization,
"total_techniques": total_techniques,
"covered": covered,
"no_coverage": no_coverage,
"coverage_pct": round(covered / total_techniques * 100, 1),
"avg_score": avg_score,
"total_rules": len(self.rules),
"generated": self.generated,
}
def get_tactic_coverage(self) -> dict:
tactic_data = {}
for tactic_id, tactic_name in ATTACK_TACTICS.items():
techniques_in_tactic = [
t for t, info in ENTERPRISE_TECHNIQUES.items()
if info["tactic"] == tactic_id
]
covered = sum(1 for t in techniques_in_tactic if t in self.technique_scores and self.technique_scores[t]["max_score"] > 0)
total = len(techniques_in_tactic)
tactic_data[tactic_name] = {
"total": total,
"covered": covered,
"pct": round(covered / max(1, total) * 100, 1),
}
return tactic_data
def get_gaps(self, min_priority: int = 0) -> list:
gaps = []
for tech_id, info in ENTERPRISE_TECHNIQUES.items():
score = self.technique_scores.get(tech_id, {}).get("max_score", 0)
if score < 50:
gaps.append({
"technique_id": tech_id,
"technique_name": info["name"],
"tactic": ATTACK_TACTICS.get(info["tactic"], "Unknown"),
"current_score": score,
"subtechniques": info["subtechniques"],
"rules_count": len(self.technique_scores.get(tech_id, {}).get("rules", [])),
})
return sorted(gaps, key=lambda x: x["current_score"])
def generate_navigator_layer(self) -> dict:
techniques = []
for tech_id, info in ENTERPRISE_TECHNIQUES.items():
score = self.technique_scores.get(tech_id, {}).get("max_score", 0)
rules = self.technique_scores.get(tech_id, {}).get("rules", [])
color = "#ff0000" if score == 0 else "#ffff00" if score < 50 else "#90ee90" if score < 75 else "#00ff00"
techniques.append({
"techniqueID": tech_id,
"color": color,
"score": score,
"comment": f"{len(rules)} rules: {', '.join(rules[:3])}" if rules else "NO DETECTION",
})
return {
"name": f"{self.organization} - Detection Coverage",
"versions": {"attack": "16", "navigator": "5.1", "layer": "4.5"},
"domain": "enterprise-attack",
"techniques": techniques,
"gradient": {"colors": ["#ff0000", "#ffff00", "#00ff00"], "minValue": 0, "maxValue": 100},
}
if __name__ == "__main__":
cmap = CoverageMap("Example Corp SOC")
rules = [
DetectionRule("Brute Force Detection", ["T1110"], 85, ["Windows Security Log"], True),
DetectionRule("Suspicious PowerShell", ["T1059"], 75, ["PowerShell Script Block"], True),
DetectionRule("New Account Created", ["T1136"], 60, ["Windows Security Log"], False),
DetectionRule("Lateral Movement RDP", ["T1021"], 70, ["Windows Security Log", "Firewall"], True),
DetectionRule("Phishing Email Detected", ["T1566"], 80, ["Email Gateway"], True),
DetectionRule("Credential Dumping", ["T1003"], 50, ["Sysmon"], False),
DetectionRule("Scheduled Task Created", ["T1053"], 65, ["Windows Security Log"], True),
DetectionRule("C2 Beaconing", ["T1071"], 45, ["Firewall", "DNS"], False),
DetectionRule("Data Exfiltration", ["T1048"], 30, ["Firewall"], False),
DetectionRule("Ransomware Encryption", ["T1486"], 40, ["EDR"], False),
]
for rule in rules:
cmap.add_rule(rule)
print("=" * 70)
print("MITRE ATT&CK COVERAGE MAP")
print("=" * 70)
summary = cmap.get_coverage_summary()
print(f"\nOrganization: {summary['organization']}")
print(f"Coverage: {summary['covered']}/{summary['total_techniques']} techniques ({summary['coverage_pct']}%)")
print(f"Average Score: {summary['avg_score']}/100")
print(f"Total Rules: {summary['total_rules']}")
print(f"\n{'Tactic':<30} {'Covered':<10} {'Total':<8} {'Coverage'}")
print("-" * 60)
for tactic, data in cmap.get_tactic_coverage().items():
if data["total"] > 0:
bar = "#" * int(data["pct"] / 5) + "." * (20 - int(data["pct"] / 5))
print(f"{tactic:<30} {data['covered']:<10} {data['total']:<8} [{bar}] {data['pct']}%")
print(f"\nDetection Gaps (Score < 50):")
for gap in cmap.get_gaps():
print(f" [{gap['current_score']:>3}] {gap['technique_id']} - {gap['technique_name']} ({gap['tactic']})")
# Export Navigator layer
layer = cmap.generate_navigator_layer()
print(f"\nATT&CK Navigator Layer exported ({len(layer['techniques'])} techniques)")