Files
Anthropic-Cybersecurity-Skills/skills/recovering-from-ransomware-attack/scripts/process.py
T

291 lines
11 KiB
Python

#!/usr/bin/env python3
"""
Ransomware Recovery Orchestration and Tracking Tool
Tracks recovery progress across multiple systems and phases:
- Recovery phase tracking with dependency management
- RTO compliance monitoring
- System validation checklists
- Recovery status reporting
"""
import json
import sys
from dataclasses import dataclass, field, asdict
from datetime import datetime, timedelta
from pathlib import Path
from typing import Optional
@dataclass
class RecoverableSystem:
name: str
tier: int
system_type: str # dc, database, application, fileserver, web, other
dependencies: list = field(default_factory=list)
rto_hours: float = 24.0
status: str = "pending" # pending, restoring, validating, online, failed
backup_source: str = ""
backup_timestamp: Optional[str] = None
restore_start: Optional[str] = None
restore_end: Optional[str] = None
validation_checks: dict = field(default_factory=dict)
notes: str = ""
@dataclass
class RecoveryPlan:
incident_id: str
organization: str
recovery_start: str
compromise_date: str
systems: list = field(default_factory=list)
phases: dict = field(default_factory=dict)
overall_status: str = "in_progress"
class RecoveryOrchestrator:
"""Manages and tracks ransomware recovery across all systems."""
def __init__(self, incident_id: str, org_name: str, compromise_date: str):
self.plan = RecoveryPlan(
incident_id=incident_id,
organization=org_name,
recovery_start=datetime.now().isoformat(),
compromise_date=compromise_date,
)
def add_system(self, system: RecoverableSystem):
self.plan.systems.append(system)
def start_restore(self, system_name: str, backup_source: str):
for sys in self.plan.systems:
if sys.name == system_name:
# Check dependencies are online
for dep in sys.dependencies:
dep_sys = next((s for s in self.plan.systems if s.name == dep), None)
if dep_sys and dep_sys.status != "online":
raise ValueError(
f"Cannot restore {system_name}: dependency {dep} "
f"is {dep_sys.status}, must be 'online'"
)
sys.status = "restoring"
sys.backup_source = backup_source
sys.restore_start = datetime.now().isoformat()
return
raise ValueError(f"System not found: {system_name}")
def complete_restore(self, system_name: str):
for sys in self.plan.systems:
if sys.name == system_name:
sys.status = "validating"
sys.restore_end = datetime.now().isoformat()
return
raise ValueError(f"System not found: {system_name}")
def validate_system(self, system_name: str, checks: dict):
"""Record validation check results for a restored system."""
for sys in self.plan.systems:
if sys.name == system_name:
sys.validation_checks = checks
all_passed = all(checks.values())
sys.status = "online" if all_passed else "failed"
return all_passed
raise ValueError(f"System not found: {system_name}")
def check_rto_compliance(self) -> list:
"""Check which systems are at risk of exceeding their RTO."""
violations = []
recovery_start = datetime.fromisoformat(self.plan.recovery_start)
for sys in self.plan.systems:
rto_deadline = recovery_start + timedelta(hours=sys.rto_hours)
if sys.status in ("pending", "restoring", "validating"):
if datetime.now() > rto_deadline:
violations.append({
"system": sys.name,
"tier": sys.tier,
"rto_hours": sys.rto_hours,
"deadline": rto_deadline.isoformat(),
"status": sys.status,
"exceeded_by_hours": round(
(datetime.now() - rto_deadline).total_seconds() / 3600, 1
),
})
return violations
def get_recovery_progress(self) -> dict:
"""Calculate overall recovery progress."""
total = len(self.plan.systems)
if total == 0:
return {"progress": 0, "by_status": {}, "by_tier": {}}
by_status = {}
by_tier = {}
for sys in self.plan.systems:
by_status[sys.status] = by_status.get(sys.status, 0) + 1
tier_key = f"tier_{sys.tier}"
if tier_key not in by_tier:
by_tier[tier_key] = {"total": 0, "online": 0}
by_tier[tier_key]["total"] += 1
if sys.status == "online":
by_tier[tier_key]["online"] += 1
online = by_status.get("online", 0)
return {
"progress": round((online / total) * 100, 1),
"total_systems": total,
"by_status": by_status,
"by_tier": by_tier,
}
def get_next_recoverable(self) -> list:
"""Get list of systems ready for recovery (dependencies met)."""
ready = []
for sys in self.plan.systems:
if sys.status != "pending":
continue
deps_met = all(
next((s for s in self.plan.systems if s.name == dep), None) is not None
and next((s for s in self.plan.systems if s.name == dep)).status == "online"
for dep in sys.dependencies
)
if deps_met or not sys.dependencies:
ready.append(sys)
return sorted(ready, key=lambda s: s.tier)
def generate_report(self) -> str:
lines = []
lines.append("=" * 70)
lines.append("RANSOMWARE RECOVERY STATUS REPORT")
lines.append("=" * 70)
lines.append(f"Incident: {self.plan.incident_id}")
lines.append(f"Organization: {self.plan.organization}")
lines.append(f"Recovery Started: {self.plan.recovery_start}")
lines.append(f"Compromise Date: {self.plan.compromise_date}")
progress = self.get_recovery_progress()
lines.append(f"\nOverall Progress: {progress['progress']}%")
lines.append(f"Total Systems: {progress['total_systems']}")
for status, count in progress["by_status"].items():
lines.append(f" {status}: {count}")
lines.append("\nBy Tier:")
for tier, data in sorted(progress["by_tier"].items()):
lines.append(f" {tier}: {data['online']}/{data['total']} online")
# RTO violations
violations = self.check_rto_compliance()
if violations:
lines.append(f"\nRTO VIOLATIONS ({len(violations)}):")
for v in violations:
lines.append(f" {v['system']} (Tier {v['tier']}): "
f"RTO {v['rto_hours']}h exceeded by {v['exceeded_by_hours']}h")
# System details
lines.append("\nSystem Recovery Status:")
lines.append("-" * 50)
for tier in sorted(set(s.tier for s in self.plan.systems)):
tier_systems = [s for s in self.plan.systems if s.tier == tier]
lines.append(f"\n Tier {tier}:")
for sys in tier_systems:
status_icon = {"pending": "[ ]", "restoring": "[~]", "validating": "[?]",
"online": "[+]", "failed": "[X]"}.get(sys.status, "[?]")
lines.append(f" {status_icon} {sys.name} ({sys.system_type}) - {sys.status}")
if sys.restore_start and sys.restore_end:
start = datetime.fromisoformat(sys.restore_start)
end = datetime.fromisoformat(sys.restore_end)
duration = (end - start).total_seconds() / 3600
lines.append(f" Restore time: {duration:.1f} hours")
if sys.validation_checks:
for check, passed in sys.validation_checks.items():
result = "PASS" if passed else "FAIL"
lines.append(f" {check}: {result}")
# Next recoverable systems
ready = self.get_next_recoverable()
if ready:
lines.append(f"\nReady for Recovery ({len(ready)}):")
for sys in ready:
lines.append(f" - {sys.name} (Tier {sys.tier}, {sys.system_type})")
lines.append("\n" + "=" * 70)
return "\n".join(lines)
def export_plan(self, output_path: str):
with open(output_path, "w") as f:
json.dump(asdict(self.plan), f, indent=2)
def main():
"""Demo recovery orchestration."""
orch = RecoveryOrchestrator(
incident_id="INC-2026-0042",
org_name="Acme Manufacturing",
compromise_date="2026-02-20T03:00:00",
)
# Define systems with dependencies
orch.add_system(RecoverableSystem(
name="DC01", tier=1, system_type="dc", rto_hours=4,
))
orch.add_system(RecoverableSystem(
name="DC02", tier=1, system_type="dc", rto_hours=4,
))
orch.add_system(RecoverableSystem(
name="DNS01", tier=1, system_type="dns", rto_hours=4,
dependencies=["DC01"],
))
orch.add_system(RecoverableSystem(
name="SQL-ERP", tier=1, system_type="database", rto_hours=8,
dependencies=["DC01", "DNS01"],
))
orch.add_system(RecoverableSystem(
name="ERP-APP", tier=1, system_type="application", rto_hours=12,
dependencies=["SQL-ERP", "DC01"],
))
orch.add_system(RecoverableSystem(
name="Exchange", tier=2, system_type="email", rto_hours=12,
dependencies=["DC01", "DC02"],
))
orch.add_system(RecoverableSystem(
name="FileServer01", tier=2, system_type="fileserver", rto_hours=24,
dependencies=["DC01"],
))
orch.add_system(RecoverableSystem(
name="WebApp01", tier=2, system_type="web", rto_hours=24,
dependencies=["SQL-ERP"],
))
orch.add_system(RecoverableSystem(
name="DevServer", tier=3, system_type="other", rto_hours=48,
dependencies=["DC01"],
))
# Simulate recovery progress
orch.start_restore("DC01", "Immutable Veeam Hardened Repo")
orch.complete_restore("DC01")
orch.validate_system("DC01", {
"dcdiag": True, "repadmin": True, "dns_resolution": True,
"krbtgt_reset": True, "persistence_scan": True,
})
orch.start_restore("DC02", "Immutable Veeam Hardened Repo")
orch.complete_restore("DC02")
orch.validate_system("DC02", {
"dcdiag": True, "repadmin": True, "replication": True,
})
orch.start_restore("DNS01", "Immutable Veeam Hardened Repo")
print(orch.generate_report())
output_path = str(Path(__file__).parent / "recovery_plan.json")
orch.export_plan(output_path)
print(f"\nRecovery plan exported to: {output_path}")
if __name__ == "__main__":
main()