mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-15 15:34:56 +03:00
c21af3347e
- Add scripts/agent.py and references/api-reference.md to all remaining skills - Update all 648 LICENSE files: copyright now reads 'Mahipal' - Add implementing-security-monitoring-with-datadog (new skill with full anatomy) - All 649 skills now have: SKILL.md, LICENSE, scripts/agent.py, references/api-reference.md
159 lines
6.2 KiB
Python
159 lines
6.2 KiB
Python
#!/usr/bin/env python3
|
|
"""Ransomware Incident Response agent — automates initial triage, IOC
|
|
collection, and containment actions during a ransomware event."""
|
|
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
|
|
RANSOMWARE_EXTENSIONS = {
|
|
".encrypted", ".locked", ".crypt", ".cry", ".crypto", ".enc",
|
|
".locky", ".cerber", ".zepto", ".wncry", ".wnry", ".wcry",
|
|
".onion", ".aaa", ".abc", ".xyz", ".zzz", ".micro", ".r5a",
|
|
}
|
|
|
|
RANSOM_NOTE_PATTERNS = [
|
|
"readme.txt", "how_to_decrypt.txt", "decrypt_instructions.html",
|
|
"restore_files.txt", "help_decrypt.html", "recovery.txt",
|
|
"_readme.txt", "how_to_recover.txt",
|
|
]
|
|
|
|
|
|
def scan_encrypted_files(target_dir: str, max_files: int = 5000) -> list[dict]:
|
|
"""Scan directory for files with ransomware-associated extensions."""
|
|
findings = []
|
|
count = 0
|
|
for root, dirs, files in os.walk(target_dir):
|
|
for fname in files:
|
|
if count >= max_files:
|
|
return findings
|
|
fpath = os.path.join(root, fname)
|
|
ext = os.path.splitext(fname)[1].lower()
|
|
if ext in RANSOMWARE_EXTENSIONS:
|
|
try:
|
|
stat = os.stat(fpath)
|
|
findings.append({
|
|
"path": fpath,
|
|
"extension": ext,
|
|
"size_bytes": stat.st_size,
|
|
"modified": datetime.fromtimestamp(stat.st_mtime).isoformat(),
|
|
})
|
|
except OSError:
|
|
pass
|
|
count += 1
|
|
return findings
|
|
|
|
|
|
def find_ransom_notes(target_dir: str) -> list[dict]:
|
|
"""Search for known ransom note filenames."""
|
|
notes = []
|
|
for root, dirs, files in os.walk(target_dir):
|
|
for fname in files:
|
|
if fname.lower() in RANSOM_NOTE_PATTERNS:
|
|
fpath = os.path.join(root, fname)
|
|
try:
|
|
with open(fpath, "r", encoding="utf-8", errors="ignore") as fh:
|
|
content = fh.read(2048)
|
|
notes.append({"path": fpath, "preview": content[:500]})
|
|
except OSError:
|
|
notes.append({"path": fpath, "preview": "[unreadable]"})
|
|
return notes
|
|
|
|
|
|
def collect_file_hashes(file_paths: list[str], max_hash: int = 100) -> list[dict]:
|
|
"""Compute SHA-256 hashes for IOC submission."""
|
|
hashes = []
|
|
for fpath in file_paths[:max_hash]:
|
|
try:
|
|
sha = hashlib.sha256()
|
|
with open(fpath, "rb") as fh:
|
|
for chunk in iter(lambda: fh.read(8192), b""):
|
|
sha.update(chunk)
|
|
hashes.append({"path": fpath, "sha256": sha.hexdigest()})
|
|
except OSError:
|
|
pass
|
|
return hashes
|
|
|
|
|
|
def check_shadow_copies(platform: str = sys.platform) -> dict:
|
|
"""Check if Volume Shadow Copies are intact (Windows) or snapshots exist."""
|
|
if platform == "win32":
|
|
try:
|
|
result = subprocess.run(
|
|
["vssadmin", "list", "shadows"],
|
|
capture_output=True, text=True, timeout=30
|
|
)
|
|
shadow_count = result.stdout.count("Shadow Copy ID")
|
|
return {"platform": "windows", "shadow_copies": shadow_count,
|
|
"intact": shadow_count > 0, "raw": result.stdout[:1000]}
|
|
except (subprocess.SubprocessError, FileNotFoundError):
|
|
return {"platform": "windows", "shadow_copies": 0, "intact": False, "error": "vssadmin unavailable"}
|
|
return {"platform": platform, "shadow_copies": -1, "intact": False, "note": "Manual check required"}
|
|
|
|
|
|
def generate_containment_actions(encrypted_count: int, notes_found: int) -> list[dict]:
|
|
"""Produce recommended containment actions based on findings."""
|
|
actions = [
|
|
{"priority": 1, "action": "Isolate affected hosts from network immediately",
|
|
"detail": "Disable network adapters or move to quarantine VLAN"},
|
|
{"priority": 2, "action": "Preserve forensic evidence before remediation",
|
|
"detail": "Create disk images of affected systems"},
|
|
{"priority": 3, "action": "Reset credentials for all privileged accounts",
|
|
"detail": "Include krbtgt, service accounts, and domain admins"},
|
|
]
|
|
if encrypted_count > 100:
|
|
actions.append({"priority": 4, "action": "Activate business continuity plan",
|
|
"detail": f"{encrypted_count} encrypted files detected — significant data impact"})
|
|
if notes_found > 0:
|
|
actions.append({"priority": 5, "action": "Collect and analyze ransom notes for variant identification",
|
|
"detail": "Submit to ID Ransomware (id-ransomware.malwarehunterteam.com)"})
|
|
return actions
|
|
|
|
|
|
def generate_report(target_dir: str, max_files: int) -> dict:
|
|
"""Run all checks and build consolidated incident report."""
|
|
encrypted = scan_encrypted_files(target_dir, max_files)
|
|
notes = find_ransom_notes(target_dir)
|
|
file_hashes = collect_file_hashes([f["path"] for f in encrypted[:50]])
|
|
shadow_status = check_shadow_copies()
|
|
actions = generate_containment_actions(len(encrypted), len(notes))
|
|
|
|
return {
|
|
"report": "ransomware_incident_response",
|
|
"generated_at": datetime.utcnow().isoformat() + "Z",
|
|
"target_directory": target_dir,
|
|
"encrypted_files_found": len(encrypted),
|
|
"ransom_notes_found": len(notes),
|
|
"shadow_copy_status": shadow_status,
|
|
"containment_actions": actions,
|
|
"encrypted_files_sample": encrypted[:20],
|
|
"ransom_notes": notes[:10],
|
|
"file_hashes": file_hashes,
|
|
}
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Ransomware Incident Response Agent")
|
|
parser.add_argument("--target", required=True, help="Directory to scan for ransomware artifacts")
|
|
parser.add_argument("--max-files", type=int, default=5000, help="Max files to scan (default: 5000)")
|
|
parser.add_argument("--output", help="Output JSON file path")
|
|
args = parser.parse_args()
|
|
|
|
report = generate_report(args.target, args.max_files)
|
|
output = json.dumps(report, indent=2)
|
|
if args.output:
|
|
Path(args.output).write_text(output, encoding="utf-8")
|
|
print(f"Report written to {args.output}")
|
|
else:
|
|
print(output)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|