Files
Anthropic-Cybersecurity-Skills/skills/performing-purple-team-exercise/scripts/agent.py
T
mukul975 c47eed6a64 Production hardening: security fixes, code quality, 724 skills complete
- Fix 25 shell=True subprocess calls with list-based commands
- Fix 49 verify=False in defensive skills (env-var override)
- Add timeout to 231 HTTP/subprocess/socket calls
- Fix 6 SQL injection patterns with whitelist validation
- Replace 8 __import__() with standard imports
- Remove 701 unused imports across 442 files
- Add authorized-testing disclaimers to all offensive skills
- Complete 11 incomplete skill directories
- Expand 10 stub SKILL.md files with full content
- Fix 2 YAML parse errors in frontmatter
- Fix 5 pre-existing syntax errors
- Convert 22 hardcoded paths/ports to environment variables
- Back up 21 redundant skill pairs to .bak
- Fix 2 global declaration errors
- 724/724 skills with full folder anatomy (SKILL.md + agent.py + api-reference.md + LICENSE)
- 0 compile errors across all 724 agent.py files
2026-03-19 13:26:49 +01:00

198 lines
7.7 KiB
Python

#!/usr/bin/env python3
"""Agent for performing purple team exercises.
Coordinates red team technique execution with blue team detection
validation, tracks ATT&CK-mapped test results, and generates
detection coverage reports.
"""
import json
import sys
from datetime import datetime
from pathlib import Path
class PurpleTeamAgent:
"""Manages purple team exercise execution and tracking."""
def __init__(self, exercise_id, output_dir):
self.exercise_id = exercise_id
self.output_dir = Path(output_dir)
self.output_dir.mkdir(parents=True, exist_ok=True)
self.test_plan = []
self.results = []
def add_technique(self, attack_id, name, tool, expected_detection):
"""Add a technique to the test plan."""
self.test_plan.append({
"attack_id": attack_id,
"name": name,
"tool": tool,
"expected_detection": expected_detection,
"status": "pending",
})
def load_test_plan(self, plan_path):
"""Load test plan from a JSON file."""
with open(plan_path, "r") as f:
data = json.load(f)
self.test_plan = data.get("techniques", [])
def build_default_test_plan(self):
"""Build a default FIN7-style purple team test plan."""
techniques = [
("T1059.001", "PowerShell Execution", "Atomic Red Team", "PowerShell alert"),
("T1053.005", "Scheduled Task", "Atomic Red Team", "Task creation alert"),
("T1547.001", "Registry Run Keys", "Atomic Red Team", "Registry modification alert"),
("T1003.001", "LSASS Memory Access", "Mimikatz", "Credential dumping alert"),
("T1550.002", "Pass-the-Hash", "Mimikatz", "NTLM anomaly detection"),
("T1021.002", "PsExec", "PsExec.exe", "PsExec service creation alert"),
("T1047", "WMI Execution", "wmic", "WMI remote execution alert"),
("T1021.001", "RDP Lateral Movement", "xfreerdp", "RDP lateral movement alert"),
("T1071.001", "Web C2 Channel", "C2 framework", "C2 beacon detection"),
("T1041", "Exfiltration over C2", "rclone", "Data exfiltration alert"),
("T1490", "Inhibit Recovery", "vssadmin", "Shadow copy deletion alert"),
("T1070.001", "Clear Event Logs", "wevtutil", "Log clearing detection"),
]
for attack_id, name, tool, detection in techniques:
self.add_technique(attack_id, name, tool, detection)
def record_execution(self, attack_id, execution_time=None):
"""Record that a red team technique has been executed."""
if execution_time is None:
execution_time = datetime.utcnow().isoformat()
for technique in self.test_plan:
if technique["attack_id"] == attack_id:
technique["execution_time"] = execution_time
technique["status"] = "executed"
break
def record_detection(self, attack_id, detected, alert_name=None,
detection_time=None, notes=""):
"""Record blue team detection result for a technique."""
if detection_time is None and detected:
detection_time = datetime.utcnow().isoformat()
for technique in self.test_plan:
if technique["attack_id"] == attack_id:
exec_time = technique.get("execution_time", "")
latency = None
if detected and exec_time and detection_time:
try:
t1 = datetime.fromisoformat(exec_time)
t2 = datetime.fromisoformat(detection_time)
latency = (t2 - t1).total_seconds()
except ValueError:
pass
result = {
"attack_id": attack_id,
"name": technique["name"],
"detected": detected,
"alert_name": alert_name,
"execution_time": exec_time,
"detection_time": detection_time,
"latency_seconds": latency,
"notes": notes,
"status": "PASS" if detected else "FAIL",
}
self.results.append(result)
technique["status"] = "detected" if detected else "gap"
return result
return None
def get_coverage_metrics(self):
"""Calculate detection coverage metrics."""
if not self.results:
return {}
total = len(self.results)
detected = sum(1 for r in self.results if r["detected"])
gaps = total - detected
latencies = [r["latency_seconds"] for r in self.results
if r["latency_seconds"] is not None]
avg_latency = sum(latencies) / len(latencies) if latencies else 0
return {
"total_techniques": total,
"detected": detected,
"gaps": gaps,
"coverage_pct": round(detected / total * 100, 1) if total else 0,
"avg_latency_seconds": round(avg_latency, 1),
"min_latency": round(min(latencies), 1) if latencies else None,
"max_latency": round(max(latencies), 1) if latencies else None,
}
def get_gap_analysis(self):
"""Identify detection gaps requiring remediation."""
return [
{
"attack_id": r["attack_id"],
"name": r["name"],
"notes": r["notes"],
"remediation": f"Create detection rule for {r['name']}",
}
for r in self.results if not r["detected"]
]
def generate_report(self):
"""Generate comprehensive purple team exercise report."""
metrics = self.get_coverage_metrics()
gaps = self.get_gap_analysis()
report = {
"exercise_id": self.exercise_id,
"report_date": datetime.utcnow().isoformat(),
"coverage_metrics": metrics,
"detailed_results": self.results,
"detection_gaps": gaps,
"test_plan": self.test_plan,
}
report_path = self.output_dir / f"{self.exercise_id}_report.json"
with open(report_path, "w") as f:
json.dump(report, f, indent=2)
print(f"PURPLE TEAM EXERCISE REPORT - {self.exercise_id}")
print("=" * 50)
print(f"Techniques Tested: {metrics.get('total_techniques', 0)}")
print(f"Detected: {metrics.get('detected', 0)} ({metrics.get('coverage_pct', 0)}%)")
print(f"Gaps: {metrics.get('gaps', 0)}")
print(f"Avg Detection Latency: {metrics.get('avg_latency_seconds', 0)}s")
print(f"\nDetailed Results:")
for r in self.results:
status = "PASS" if r["detected"] else "FAIL"
latency = f"{r['latency_seconds']}s" if r["latency_seconds"] else "N/A"
print(f" [{status}] {r['attack_id']} {r['name']} (Latency: {latency})")
if gaps:
print(f"\nDetection Gaps:")
for g in gaps:
print(f" - {g['attack_id']} {g['name']}: {g['notes']}")
return report
def main():
if len(sys.argv) < 2:
print("Usage: agent.py <exercise_id> [output_dir] [plan_file]")
sys.exit(1)
exercise_id = sys.argv[1]
output_dir = sys.argv[2] if len(sys.argv) > 2 else "./purple_team_output"
agent = PurpleTeamAgent(exercise_id, output_dir)
if len(sys.argv) > 3:
agent.load_test_plan(sys.argv[3])
else:
agent.build_default_test_plan()
print(json.dumps({"test_plan": agent.test_plan}, indent=2))
print(f"\nTest plan created with {len(agent.test_plan)} techniques")
print(f"Output directory: {output_dir}")
if __name__ == "__main__":
main()