mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-14 11:51:16 +03:00
513 lines
21 KiB
Python
513 lines
21 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Red Team Engagement Tracker and Reporter
|
|
|
|
Tracks red team activities, maps them to MITRE ATT&CK techniques,
|
|
and generates engagement reports with detection gap analysis.
|
|
"""
|
|
|
|
import json
|
|
import csv
|
|
import os
|
|
import hashlib
|
|
from datetime import datetime, timedelta
|
|
from typing import Optional
|
|
from dataclasses import dataclass, field, asdict
|
|
|
|
|
|
@dataclass
|
|
class RedTeamAction:
|
|
"""Represents a single red team action during an engagement."""
|
|
timestamp: str
|
|
phase: str
|
|
mitre_tactic: str
|
|
mitre_technique_id: str
|
|
mitre_technique_name: str
|
|
description: str
|
|
target_host: str
|
|
tool_used: str
|
|
outcome: str # success, failure, partial
|
|
detected: bool = False
|
|
detection_time: Optional[str] = None
|
|
detection_source: Optional[str] = None
|
|
evidence_path: Optional[str] = None
|
|
operator: str = ""
|
|
notes: str = ""
|
|
|
|
|
|
@dataclass
|
|
class EngagementConfig:
|
|
"""Configuration for a red team engagement."""
|
|
engagement_id: str
|
|
client_name: str
|
|
start_date: str
|
|
end_date: str
|
|
scope: list = field(default_factory=list)
|
|
out_of_scope: list = field(default_factory=list)
|
|
objectives: list = field(default_factory=list)
|
|
threat_profile: str = ""
|
|
emergency_contact: str = ""
|
|
roe_version: str = ""
|
|
|
|
|
|
MITRE_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",
|
|
"TA0010": "Exfiltration",
|
|
"TA0011": "Command and Control",
|
|
"TA0040": "Impact",
|
|
}
|
|
|
|
COMMON_TECHNIQUES = {
|
|
"T1566.001": {"name": "Phishing: Spearphishing Attachment", "tactic": "TA0001"},
|
|
"T1566.002": {"name": "Phishing: Spearphishing Link", "tactic": "TA0001"},
|
|
"T1190": {"name": "Exploit Public-Facing Application", "tactic": "TA0001"},
|
|
"T1078": {"name": "Valid Accounts", "tactic": "TA0001"},
|
|
"T1059.001": {"name": "PowerShell", "tactic": "TA0002"},
|
|
"T1059.003": {"name": "Windows Command Shell", "tactic": "TA0002"},
|
|
"T1047": {"name": "Windows Management Instrumentation", "tactic": "TA0002"},
|
|
"T1053.005": {"name": "Scheduled Task", "tactic": "TA0003"},
|
|
"T1547.001": {"name": "Registry Run Keys", "tactic": "TA0003"},
|
|
"T1068": {"name": "Exploitation for Privilege Escalation", "tactic": "TA0004"},
|
|
"T1548.002": {"name": "Bypass User Account Control", "tactic": "TA0004"},
|
|
"T1055": {"name": "Process Injection", "tactic": "TA0005"},
|
|
"T1027": {"name": "Obfuscated Files or Information", "tactic": "TA0005"},
|
|
"T1562.001": {"name": "Disable or Modify Tools", "tactic": "TA0005"},
|
|
"T1003.001": {"name": "LSASS Memory", "tactic": "TA0006"},
|
|
"T1003.006": {"name": "DCSync", "tactic": "TA0006"},
|
|
"T1558.003": {"name": "Kerberoasting", "tactic": "TA0006"},
|
|
"T1087.002": {"name": "Domain Account Discovery", "tactic": "TA0007"},
|
|
"T1018": {"name": "Remote System Discovery", "tactic": "TA0007"},
|
|
"T1082": {"name": "System Information Discovery", "tactic": "TA0007"},
|
|
"T1021.002": {"name": "SMB/Windows Admin Shares", "tactic": "TA0008"},
|
|
"T1021.001": {"name": "Remote Desktop Protocol", "tactic": "TA0008"},
|
|
"T1550.002": {"name": "Pass the Hash", "tactic": "TA0008"},
|
|
"T1560": {"name": "Archive Collected Data", "tactic": "TA0009"},
|
|
"T1041": {"name": "Exfiltration Over C2 Channel", "tactic": "TA0010"},
|
|
"T1048.003": {"name": "Exfiltration Over Unencrypted Non-C2 Protocol", "tactic": "TA0010"},
|
|
"T1486": {"name": "Data Encrypted for Impact", "tactic": "TA0040"},
|
|
}
|
|
|
|
|
|
class RedTeamEngagementTracker:
|
|
"""Tracks and reports on red team engagement activities."""
|
|
|
|
def __init__(self, config: EngagementConfig, output_dir: str = "./engagement_output"):
|
|
self.config = config
|
|
self.actions: list[RedTeamAction] = []
|
|
self.output_dir = output_dir
|
|
os.makedirs(output_dir, exist_ok=True)
|
|
|
|
def log_action(self, action: RedTeamAction) -> None:
|
|
"""Log a red team action."""
|
|
self.actions.append(action)
|
|
self._append_to_log(action)
|
|
print(f"[+] Logged: {action.mitre_technique_id} - {action.description}")
|
|
|
|
def _append_to_log(self, action: RedTeamAction) -> None:
|
|
"""Append action to engagement log file."""
|
|
log_path = os.path.join(self.output_dir, f"{self.config.engagement_id}_log.jsonl")
|
|
with open(log_path, "a") as f:
|
|
f.write(json.dumps(asdict(action)) + "\n")
|
|
|
|
def calculate_metrics(self) -> dict:
|
|
"""Calculate engagement metrics including MTTD, MTTR, and detection coverage."""
|
|
total_actions = len(self.actions)
|
|
detected_actions = [a for a in self.actions if a.detected]
|
|
successful_actions = [a for a in self.actions if a.outcome == "success"]
|
|
|
|
# Calculate Mean Time to Detect (MTTD)
|
|
detection_deltas = []
|
|
for action in detected_actions:
|
|
if action.detection_time and action.timestamp:
|
|
action_time = datetime.fromisoformat(action.timestamp)
|
|
detect_time = datetime.fromisoformat(action.detection_time)
|
|
delta = (detect_time - action_time).total_seconds() / 3600 # hours
|
|
detection_deltas.append(delta)
|
|
|
|
mttd_hours = sum(detection_deltas) / len(detection_deltas) if detection_deltas else None
|
|
|
|
# Calculate TTP coverage
|
|
unique_techniques = set(a.mitre_technique_id for a in self.actions)
|
|
detected_techniques = set(a.mitre_technique_id for a in detected_actions)
|
|
ttp_coverage = (
|
|
len(detected_techniques) / len(unique_techniques) * 100
|
|
if unique_techniques
|
|
else 0
|
|
)
|
|
|
|
# Tactic-level detection rates
|
|
tactic_stats = {}
|
|
for tactic_id, tactic_name in MITRE_TACTICS.items():
|
|
tactic_actions = [a for a in self.actions if a.mitre_tactic == tactic_id]
|
|
tactic_detected = [a for a in tactic_actions if a.detected]
|
|
if tactic_actions:
|
|
tactic_stats[tactic_name] = {
|
|
"total": len(tactic_actions),
|
|
"detected": len(tactic_detected),
|
|
"rate": len(tactic_detected) / len(tactic_actions) * 100,
|
|
}
|
|
|
|
# Objective completion
|
|
objectives_achieved = sum(
|
|
1 for obj in self.config.objectives if obj.get("achieved", False)
|
|
)
|
|
|
|
return {
|
|
"engagement_id": self.config.engagement_id,
|
|
"total_actions": total_actions,
|
|
"successful_actions": len(successful_actions),
|
|
"success_rate": len(successful_actions) / total_actions * 100 if total_actions else 0,
|
|
"detected_actions": len(detected_actions),
|
|
"detection_rate": len(detected_actions) / total_actions * 100 if total_actions else 0,
|
|
"mttd_hours": mttd_hours,
|
|
"unique_techniques_used": len(unique_techniques),
|
|
"unique_techniques_detected": len(detected_techniques),
|
|
"ttp_coverage_pct": ttp_coverage,
|
|
"tactic_detection_rates": tactic_stats,
|
|
"objectives_total": len(self.config.objectives),
|
|
"objectives_achieved": objectives_achieved,
|
|
"undetected_techniques": list(unique_techniques - detected_techniques),
|
|
}
|
|
|
|
def generate_attack_heatmap(self) -> dict:
|
|
"""Generate MITRE ATT&CK heatmap data for Navigator."""
|
|
techniques = {}
|
|
for action in self.actions:
|
|
tid = action.mitre_technique_id
|
|
if tid not in techniques:
|
|
techniques[tid] = {
|
|
"techniqueID": tid,
|
|
"score": 0,
|
|
"color": "",
|
|
"comment": "",
|
|
"metadata": [],
|
|
}
|
|
techniques[tid]["score"] += 1
|
|
if action.detected:
|
|
techniques[tid]["color"] = "#ff6666" # Red for detected
|
|
techniques[tid]["comment"] += f"DETECTED by {action.detection_source}; "
|
|
else:
|
|
techniques[tid]["color"] = "#66ff66" # Green for undetected
|
|
techniques[tid]["comment"] += f"UNDETECTED: {action.description}; "
|
|
|
|
navigator_layer = {
|
|
"name": f"Red Team - {self.config.engagement_id}",
|
|
"versions": {"attack": "14", "navigator": "4.9.1", "layer": "4.5"},
|
|
"domain": "enterprise-attack",
|
|
"description": f"Red team engagement for {self.config.client_name}",
|
|
"techniques": list(techniques.values()),
|
|
"gradient": {
|
|
"colors": ["#66ff66", "#ffff66", "#ff6666"],
|
|
"minValue": 0,
|
|
"maxValue": 5,
|
|
},
|
|
}
|
|
|
|
output_path = os.path.join(
|
|
self.output_dir, f"{self.config.engagement_id}_navigator.json"
|
|
)
|
|
with open(output_path, "w") as f:
|
|
json.dump(navigator_layer, f, indent=2)
|
|
|
|
print(f"[+] ATT&CK Navigator layer saved to: {output_path}")
|
|
return navigator_layer
|
|
|
|
def generate_detection_gap_report(self) -> str:
|
|
"""Generate a detection gap analysis report."""
|
|
metrics = self.calculate_metrics()
|
|
lines = []
|
|
lines.append("=" * 70)
|
|
lines.append("DETECTION GAP ANALYSIS REPORT")
|
|
lines.append(f"Engagement: {self.config.engagement_id}")
|
|
lines.append(f"Client: {self.config.client_name}")
|
|
lines.append(f"Period: {self.config.start_date} to {self.config.end_date}")
|
|
lines.append("=" * 70)
|
|
lines.append("")
|
|
lines.append(f"Overall Detection Rate: {metrics['detection_rate']:.1f}%")
|
|
lines.append(f"TTP Coverage: {metrics['ttp_coverage_pct']:.1f}%")
|
|
if metrics["mttd_hours"]:
|
|
lines.append(f"Mean Time to Detect: {metrics['mttd_hours']:.1f} hours")
|
|
lines.append("")
|
|
lines.append("-" * 70)
|
|
lines.append("TACTIC-LEVEL DETECTION RATES")
|
|
lines.append("-" * 70)
|
|
|
|
for tactic_name, stats in metrics.get("tactic_detection_rates", {}).items():
|
|
bar = "#" * int(stats["rate"] / 5) + "-" * (20 - int(stats["rate"] / 5))
|
|
lines.append(
|
|
f" {tactic_name:<25} [{bar}] {stats['rate']:.0f}% "
|
|
f"({stats['detected']}/{stats['total']})"
|
|
)
|
|
|
|
lines.append("")
|
|
lines.append("-" * 70)
|
|
lines.append("UNDETECTED TECHNIQUES (GAPS)")
|
|
lines.append("-" * 70)
|
|
|
|
for tid in metrics.get("undetected_techniques", []):
|
|
tech_info = COMMON_TECHNIQUES.get(tid, {})
|
|
name = tech_info.get("name", "Unknown")
|
|
lines.append(f" [!] {tid} - {name}")
|
|
relevant_actions = [
|
|
a for a in self.actions if a.mitre_technique_id == tid and not a.detected
|
|
]
|
|
for action in relevant_actions:
|
|
lines.append(f" Tool: {action.tool_used} | Target: {action.target_host}")
|
|
|
|
lines.append("")
|
|
lines.append("-" * 70)
|
|
lines.append("RECOMMENDATIONS")
|
|
lines.append("-" * 70)
|
|
|
|
for tid in metrics.get("undetected_techniques", []):
|
|
tech_info = COMMON_TECHNIQUES.get(tid, {})
|
|
name = tech_info.get("name", "Unknown")
|
|
lines.append(f" [{tid}] {name}")
|
|
if tid == "T1003.001":
|
|
lines.append(" -> Enable Credential Guard and LSASS protection")
|
|
lines.append(" -> Deploy Sysmon with EventID 10 (ProcessAccess) rules")
|
|
elif tid == "T1558.003":
|
|
lines.append(" -> Use Group Managed Service Accounts (gMSA)")
|
|
lines.append(" -> Monitor EventID 4769 for RC4 ticket requests")
|
|
elif tid == "T1055":
|
|
lines.append(" -> Deploy EDR with memory scanning capabilities")
|
|
lines.append(" -> Monitor for cross-process injection (Sysmon EventID 8)")
|
|
elif tid == "T1021.002":
|
|
lines.append(" -> Restrict lateral movement with Windows Firewall")
|
|
lines.append(" -> Monitor for EventID 5140/5145 (network share access)")
|
|
elif tid == "T1550.002":
|
|
lines.append(" -> Enable Restricted Admin Mode for RDP")
|
|
lines.append(" -> Deploy Windows Defender Credential Guard")
|
|
else:
|
|
lines.append(" -> Review MITRE ATT&CK mitigations for this technique")
|
|
lines.append(" -> Create detection rule in SIEM for related events")
|
|
|
|
report_text = "\n".join(lines)
|
|
|
|
report_path = os.path.join(
|
|
self.output_dir, f"{self.config.engagement_id}_gap_report.txt"
|
|
)
|
|
with open(report_path, "w") as f:
|
|
f.write(report_text)
|
|
|
|
print(f"[+] Gap report saved to: {report_path}")
|
|
return report_text
|
|
|
|
def export_timeline_csv(self) -> str:
|
|
"""Export engagement timeline as CSV for analysis."""
|
|
csv_path = os.path.join(
|
|
self.output_dir, f"{self.config.engagement_id}_timeline.csv"
|
|
)
|
|
with open(csv_path, "w", newline="") as f:
|
|
writer = csv.writer(f)
|
|
writer.writerow([
|
|
"Timestamp", "Phase", "Tactic", "Technique ID", "Technique Name",
|
|
"Description", "Target", "Tool", "Outcome", "Detected",
|
|
"Detection Time", "Detection Source", "Operator",
|
|
])
|
|
for action in sorted(self.actions, key=lambda a: a.timestamp):
|
|
writer.writerow([
|
|
action.timestamp, action.phase, action.mitre_tactic,
|
|
action.mitre_technique_id, action.mitre_technique_name,
|
|
action.description, action.target_host, action.tool_used,
|
|
action.outcome, action.detected, action.detection_time,
|
|
action.detection_source, action.operator,
|
|
])
|
|
|
|
print(f"[+] Timeline CSV saved to: {csv_path}")
|
|
return csv_path
|
|
|
|
def generate_executive_summary(self) -> str:
|
|
"""Generate executive summary for leadership."""
|
|
metrics = self.calculate_metrics()
|
|
summary = f"""
|
|
EXECUTIVE SUMMARY - RED TEAM ENGAGEMENT
|
|
========================================
|
|
Engagement ID: {self.config.engagement_id}
|
|
Client: {self.config.client_name}
|
|
Period: {self.config.start_date} to {self.config.end_date}
|
|
Threat Profile: {self.config.threat_profile}
|
|
|
|
KEY FINDINGS:
|
|
- {metrics['unique_techniques_used']} unique ATT&CK techniques were executed
|
|
- {metrics['detection_rate']:.0f}% of red team actions were detected by the SOC
|
|
- {metrics['ttp_coverage_pct']:.0f}% of techniques used had at least one detection
|
|
- {len(metrics.get('undetected_techniques', []))} technique(s) had ZERO detection coverage
|
|
- {metrics['objectives_achieved']}/{metrics['objectives_total']} engagement objectives achieved
|
|
|
|
RISK RATING: {'CRITICAL' if metrics['detection_rate'] < 30 else 'HIGH' if metrics['detection_rate'] < 50 else 'MEDIUM' if metrics['detection_rate'] < 70 else 'LOW'}
|
|
|
|
The red team {'successfully' if metrics['success_rate'] > 50 else 'partially'} achieved the defined objectives,
|
|
demonstrating that the organization's current security posture requires
|
|
{'immediate remediation' if metrics['detection_rate'] < 30 else 'significant improvement' if metrics['detection_rate'] < 50 else 'targeted improvements' if metrics['detection_rate'] < 70 else 'minor tuning'}.
|
|
"""
|
|
return summary
|
|
|
|
|
|
def main():
|
|
"""Demonstrate the engagement tracker with sample data."""
|
|
config = EngagementConfig(
|
|
engagement_id="RT-2025-001",
|
|
client_name="Example Corp",
|
|
start_date="2025-01-15",
|
|
end_date="2025-02-15",
|
|
scope=["10.0.0.0/8", "*.example.com"],
|
|
out_of_scope=["10.0.99.0/24", "production-db.example.com"],
|
|
objectives=[
|
|
{"name": "Achieve Domain Admin", "achieved": True},
|
|
{"name": "Exfiltrate PII data", "achieved": True},
|
|
{"name": "Access SCADA network", "achieved": False},
|
|
],
|
|
threat_profile="APT29 (Cozy Bear)",
|
|
emergency_contact="CISO: +1-555-0100",
|
|
)
|
|
|
|
tracker = RedTeamEngagementTracker(config)
|
|
|
|
sample_actions = [
|
|
RedTeamAction(
|
|
timestamp="2025-01-16T09:00:00",
|
|
phase="reconnaissance",
|
|
mitre_tactic="TA0043",
|
|
mitre_technique_id="T1593",
|
|
mitre_technique_name="Search Open Websites/Domains",
|
|
description="Subdomain enumeration using Amass",
|
|
target_host="example.com",
|
|
tool_used="Amass",
|
|
outcome="success",
|
|
detected=False,
|
|
operator="operator1",
|
|
),
|
|
RedTeamAction(
|
|
timestamp="2025-01-20T14:30:00",
|
|
phase="initial_access",
|
|
mitre_tactic="TA0001",
|
|
mitre_technique_id="T1566.001",
|
|
mitre_technique_name="Spearphishing Attachment",
|
|
description="Sent phishing email with macro-enabled document to HR team",
|
|
target_host="mail.example.com",
|
|
tool_used="GoPhish",
|
|
outcome="success",
|
|
detected=True,
|
|
detection_time="2025-01-20T15:45:00",
|
|
detection_source="Proofpoint Email Gateway",
|
|
operator="operator1",
|
|
),
|
|
RedTeamAction(
|
|
timestamp="2025-01-22T10:00:00",
|
|
phase="execution",
|
|
mitre_tactic="TA0002",
|
|
mitre_technique_id="T1059.001",
|
|
mitre_technique_name="PowerShell",
|
|
description="Executed PowerShell stager on workstation WS-042",
|
|
target_host="WS-042.example.com",
|
|
tool_used="Havoc C2",
|
|
outcome="success",
|
|
detected=False,
|
|
operator="operator2",
|
|
),
|
|
RedTeamAction(
|
|
timestamp="2025-01-23T08:15:00",
|
|
phase="credential_access",
|
|
mitre_tactic="TA0006",
|
|
mitre_technique_id="T1003.001",
|
|
mitre_technique_name="LSASS Memory",
|
|
description="Dumped LSASS process memory using SafetyKatz",
|
|
target_host="WS-042.example.com",
|
|
tool_used="SafetyKatz",
|
|
outcome="success",
|
|
detected=True,
|
|
detection_time="2025-01-23T08:20:00",
|
|
detection_source="CrowdStrike Falcon",
|
|
operator="operator2",
|
|
),
|
|
RedTeamAction(
|
|
timestamp="2025-01-24T11:00:00",
|
|
phase="credential_access",
|
|
mitre_tactic="TA0006",
|
|
mitre_technique_id="T1558.003",
|
|
mitre_technique_name="Kerberoasting",
|
|
description="Kerberoasted 15 service accounts using Rubeus",
|
|
target_host="DC01.example.com",
|
|
tool_used="Rubeus",
|
|
outcome="success",
|
|
detected=False,
|
|
operator="operator1",
|
|
),
|
|
RedTeamAction(
|
|
timestamp="2025-01-25T14:00:00",
|
|
phase="lateral_movement",
|
|
mitre_tactic="TA0008",
|
|
mitre_technique_id="T1021.002",
|
|
mitre_technique_name="SMB/Windows Admin Shares",
|
|
description="Lateral movement to file server via PsExec",
|
|
target_host="FS01.example.com",
|
|
tool_used="Impacket PsExec",
|
|
outcome="success",
|
|
detected=False,
|
|
operator="operator2",
|
|
),
|
|
RedTeamAction(
|
|
timestamp="2025-01-28T09:30:00",
|
|
phase="credential_access",
|
|
mitre_tactic="TA0006",
|
|
mitre_technique_id="T1003.006",
|
|
mitre_technique_name="DCSync",
|
|
description="DCSync to extract all domain password hashes",
|
|
target_host="DC01.example.com",
|
|
tool_used="Impacket secretsdump",
|
|
outcome="success",
|
|
detected=True,
|
|
detection_time="2025-01-28T10:15:00",
|
|
detection_source="Microsoft Defender for Identity",
|
|
operator="operator1",
|
|
),
|
|
RedTeamAction(
|
|
timestamp="2025-01-30T16:00:00",
|
|
phase="exfiltration",
|
|
mitre_tactic="TA0010",
|
|
mitre_technique_id="T1041",
|
|
mitre_technique_name="Exfiltration Over C2 Channel",
|
|
description="Exfiltrated 50MB of staged PII data over C2 HTTPS",
|
|
target_host="FS01.example.com",
|
|
tool_used="Havoc C2",
|
|
outcome="success",
|
|
detected=False,
|
|
operator="operator2",
|
|
),
|
|
]
|
|
|
|
for action in sample_actions:
|
|
tracker.log_action(action)
|
|
|
|
print("\n" + "=" * 70)
|
|
print("GENERATING REPORTS")
|
|
print("=" * 70)
|
|
|
|
tracker.generate_attack_heatmap()
|
|
tracker.export_timeline_csv()
|
|
print(tracker.generate_detection_gap_report())
|
|
print(tracker.generate_executive_summary())
|
|
|
|
metrics = tracker.calculate_metrics()
|
|
metrics_path = os.path.join(
|
|
tracker.output_dir, f"{config.engagement_id}_metrics.json"
|
|
)
|
|
with open(metrics_path, "w") as f:
|
|
json.dump(metrics, f, indent=2, default=str)
|
|
print(f"[+] Metrics saved to: {metrics_path}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|