mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-10 21:24:56 +03:00
27c6414ca5
Complete skill folder anatomy across all cybersecurity skills: - scripts/agent.py: 80-150 line Python agents using real libraries (impacket, boto3, azure-mgmt-*, kubernetes, pefile, yara, scapy, shodan, stix2, etc.) - references/api-reference.md: real API documentation with method signatures - LICENSE: MIT license for all skill folders
207 lines
7.7 KiB
Python
207 lines
7.7 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Ransomware Attack Artifact Investigation Agent
|
|
Collects and analyzes ransomware artifacts including ransom notes, encrypted
|
|
file samples, registry modifications, and event logs to identify the variant,
|
|
attack vector, and encryption scope.
|
|
"""
|
|
|
|
import hashlib
|
|
import json
|
|
import os
|
|
import re
|
|
import sys
|
|
from datetime import datetime, timezone
|
|
from pathlib import Path
|
|
|
|
import requests
|
|
|
|
|
|
RANSOMWARE_ID_URL = "https://id-ransomware.malwarehunterteam.com/api/"
|
|
VT_API_KEY = ""
|
|
|
|
|
|
def collect_ransom_notes(search_root: str) -> list[dict]:
|
|
"""Search filesystem for common ransom note filenames."""
|
|
ransom_note_patterns = [
|
|
"README.txt", "DECRYPT*.txt", "HOW_TO_DECRYPT*", "RECOVER*",
|
|
"_readme.txt", "!README!*", "HELP_DECRYPT*", "YOUR_FILES*",
|
|
"ATTENTION*.txt", "RESTORE*FILES*", "#DECRYPT#*", "info.hta",
|
|
]
|
|
found_notes = []
|
|
root = Path(search_root)
|
|
|
|
for pattern in ransom_note_patterns:
|
|
for match in root.rglob(pattern):
|
|
if match.is_file() and match.stat().st_size < 1_000_000:
|
|
with open(match, "r", errors="ignore") as f:
|
|
content = f.read(4096)
|
|
found_notes.append({
|
|
"path": str(match),
|
|
"filename": match.name,
|
|
"size": match.stat().st_size,
|
|
"content_preview": content[:500],
|
|
"sha256": hashlib.sha256(content.encode()).hexdigest(),
|
|
})
|
|
|
|
return found_notes
|
|
|
|
|
|
def identify_encrypted_files(search_root: str) -> dict:
|
|
"""Identify encrypted files by extension and calculate scope."""
|
|
known_extensions = [
|
|
".encrypted", ".locked", ".crypto", ".crypt", ".enc",
|
|
".locky", ".zepto", ".cerber", ".dharma", ".phobos",
|
|
".ryuk", ".conti", ".lockbit", ".blackcat", ".hive",
|
|
".akira", ".royal", ".play", ".clop", ".alphv",
|
|
]
|
|
encrypted_files = []
|
|
extension_counts = {}
|
|
total_size = 0
|
|
|
|
root = Path(search_root)
|
|
for filepath in root.rglob("*"):
|
|
if filepath.is_file():
|
|
ext = filepath.suffix.lower()
|
|
if ext in known_extensions:
|
|
encrypted_files.append(str(filepath))
|
|
extension_counts[ext] = extension_counts.get(ext, 0) + 1
|
|
total_size += filepath.stat().st_size
|
|
|
|
return {
|
|
"total_encrypted_files": len(encrypted_files),
|
|
"total_encrypted_size_gb": round(total_size / (1024**3), 2),
|
|
"extensions_found": extension_counts,
|
|
"sample_files": encrypted_files[:20],
|
|
}
|
|
|
|
|
|
def analyze_ransom_note_content(notes: list[dict]) -> dict:
|
|
"""Extract IOCs and payment details from ransom notes."""
|
|
bitcoin_pattern = re.compile(r"[13][a-km-zA-HJ-NP-Z1-9]{25,34}|bc1[a-z0-9]{39,59}")
|
|
monero_pattern = re.compile(r"4[0-9AB][1-9A-HJ-NP-Za-km-z]{93}")
|
|
tor_pattern = re.compile(r"[a-z2-7]{16,56}\.onion")
|
|
email_pattern = re.compile(r"[\w.+-]+@[\w-]+\.[a-zA-Z]{2,}")
|
|
|
|
iocs = {"bitcoin_addresses": set(), "monero_addresses": set(),
|
|
"tor_sites": set(), "email_contacts": set(), "ransom_amounts": []}
|
|
|
|
for note in notes:
|
|
content = note.get("content_preview", "")
|
|
for match in bitcoin_pattern.findall(content):
|
|
iocs["bitcoin_addresses"].add(match)
|
|
for match in monero_pattern.findall(content):
|
|
iocs["monero_addresses"].add(match)
|
|
for match in tor_pattern.findall(content):
|
|
iocs["tor_sites"].add(match)
|
|
for match in email_pattern.findall(content):
|
|
iocs["email_contacts"].add(match)
|
|
|
|
amount_match = re.search(r"\$\s?([\d,]+)", content)
|
|
if amount_match:
|
|
iocs["ransom_amounts"].append(amount_match.group(0))
|
|
|
|
return {k: sorted(v) if isinstance(v, set) else v for k, v in iocs.items()}
|
|
|
|
|
|
def check_hash_virustotal(file_hash: str, api_key: str) -> dict:
|
|
"""Look up file hash on VirusTotal for ransomware identification."""
|
|
if not api_key:
|
|
return {"error": "VT_API_KEY not configured"}
|
|
resp = requests.get(
|
|
f"https://www.virustotal.com/api/v3/files/{file_hash}",
|
|
headers={"x-apikey": api_key}, timeout=30,
|
|
)
|
|
if resp.status_code == 200:
|
|
attrs = resp.json().get("data", {}).get("attributes", {})
|
|
return {
|
|
"threat_label": attrs.get("popular_threat_classification", {}).get(
|
|
"suggested_threat_label", "unknown"),
|
|
"detection_ratio": f"{attrs.get('last_analysis_stats', {}).get('malicious', 0)}"
|
|
f"/{sum(attrs.get('last_analysis_stats', {}).values())}",
|
|
"first_seen": attrs.get("first_submission_date", ""),
|
|
"names": attrs.get("names", [])[:5],
|
|
}
|
|
return {"error": f"VT lookup failed: {resp.status_code}"}
|
|
|
|
|
|
def parse_windows_event_logs(evtx_export_path: str) -> list[dict]:
|
|
"""Parse exported Windows event log CSV for ransomware indicators."""
|
|
events = []
|
|
if not os.path.exists(evtx_export_path):
|
|
return events
|
|
|
|
import csv
|
|
with open(evtx_export_path, "r", newline="", errors="ignore") as f:
|
|
reader = csv.DictReader(f)
|
|
for row in reader:
|
|
event_id = row.get("EventID", row.get("event_id", ""))
|
|
suspicious_ids = ["1102", "4688", "4697", "7045", "1116", "4624"]
|
|
if str(event_id) in suspicious_ids:
|
|
events.append({
|
|
"timestamp": row.get("TimeCreated", row.get("timestamp", "")),
|
|
"event_id": event_id,
|
|
"source": row.get("ProviderName", row.get("source", "")),
|
|
"message": row.get("Message", row.get("message", ""))[:300],
|
|
})
|
|
|
|
return events
|
|
|
|
|
|
def generate_report(notes: list, encrypted: dict, iocs: dict, events: list) -> str:
|
|
"""Generate ransomware investigation report."""
|
|
lines = [
|
|
"RANSOMWARE ATTACK ARTIFACT INVESTIGATION REPORT",
|
|
"=" * 55,
|
|
f"Investigation Date: {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M UTC')}",
|
|
"",
|
|
"RANSOM NOTES:",
|
|
f" Notes Found: {len(notes)}",
|
|
]
|
|
for note in notes[:5]:
|
|
lines.append(f" - {note['filename']} ({note['path']})")
|
|
|
|
lines.extend([
|
|
"",
|
|
"ENCRYPTION SCOPE:",
|
|
f" Encrypted Files: {encrypted['total_encrypted_files']}",
|
|
f" Total Size: {encrypted['total_encrypted_size_gb']} GB",
|
|
f" Extensions: {json.dumps(encrypted['extensions_found'])}",
|
|
"",
|
|
"EXTRACTED IOCs:",
|
|
f" Bitcoin Addresses: {len(iocs.get('bitcoin_addresses', []))}",
|
|
f" Tor Sites: {len(iocs.get('tor_sites', []))}",
|
|
f" Contact Emails: {len(iocs.get('email_contacts', []))}",
|
|
"",
|
|
f"SUSPICIOUS EVENTS: {len(events)}",
|
|
])
|
|
for evt in events[:10]:
|
|
lines.append(f" [{evt['event_id']}] {evt['timestamp']} - {evt['message'][:80]}")
|
|
|
|
return "\n".join(lines)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
VT_API_KEY = os.getenv("VT_API_KEY", VT_API_KEY)
|
|
search_root = sys.argv[1] if len(sys.argv) > 1 else "."
|
|
evtx_path = sys.argv[2] if len(sys.argv) > 2 else "events.csv"
|
|
|
|
print(f"[*] Investigating ransomware artifacts in: {search_root}")
|
|
|
|
notes = collect_ransom_notes(search_root)
|
|
print(f"[*] Found {len(notes)} ransom notes")
|
|
|
|
encrypted = identify_encrypted_files(search_root)
|
|
print(f"[*] Found {encrypted['total_encrypted_files']} encrypted files")
|
|
|
|
iocs = analyze_ransom_note_content(notes)
|
|
events = parse_windows_event_logs(evtx_path)
|
|
|
|
report = generate_report(notes, encrypted, iocs, events)
|
|
print(report)
|
|
|
|
output = f"ransomware_investigation_{datetime.now(timezone.utc).strftime('%Y%m%d')}.json"
|
|
with open(output, "w") as f:
|
|
json.dump({"ransom_notes": notes, "encrypted_files": encrypted, "iocs": iocs, "events": events}, f, indent=2)
|
|
print(f"\n[*] Results saved to {output}")
|