mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-14 11:51:16 +03:00
c47eed6a64
- 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
162 lines
5.9 KiB
Python
162 lines
5.9 KiB
Python
#!/usr/bin/env python3
|
|
"""Ransomware Playbook Automation Agent - Automates SOC ransomware response steps."""
|
|
|
|
import json
|
|
import logging
|
|
import os
|
|
import argparse
|
|
import hashlib
|
|
from datetime import datetime
|
|
|
|
import requests
|
|
|
|
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def check_id_ransomware(sample_path):
|
|
"""Upload encrypted file sample to ID Ransomware for variant identification."""
|
|
url = "https://id-ransomware.malwarehunterteam.com/index.php"
|
|
with open(sample_path, "rb") as f:
|
|
files = {"sampleUpload": (sample_path, f)}
|
|
resp = requests.post(url, files=files, timeout=60)
|
|
logger.info("ID Ransomware response status: %d", resp.status_code)
|
|
return resp.text
|
|
|
|
|
|
def query_nomoreransom(ransomware_family):
|
|
"""Check No More Ransom Project for available decryptors."""
|
|
url = f"https://www.nomoreransom.org/en/decryption-tools.html"
|
|
resp = requests.get(url, timeout=30)
|
|
if ransomware_family.lower() in resp.text.lower():
|
|
logger.info("Decryptor may be available for %s on No More Ransom", ransomware_family)
|
|
return True
|
|
logger.info("No decryptor found for %s", ransomware_family)
|
|
return False
|
|
|
|
|
|
def query_malwarebazaar_hash(file_hash):
|
|
"""Query MalwareBazaar for IOC details by SHA-256 hash."""
|
|
url = "https://mb-api.abuse.ch/api/v1/"
|
|
data = {"query": "get_info", "hash": file_hash}
|
|
resp = requests.post(url, data=data, timeout=30)
|
|
result = resp.json()
|
|
if result.get("query_status") == "ok":
|
|
sample = result["data"][0]
|
|
logger.info(
|
|
"MalwareBazaar match: %s (family: %s)",
|
|
sample.get("sha256_hash"),
|
|
sample.get("signature"),
|
|
)
|
|
return sample
|
|
return None
|
|
|
|
|
|
def isolate_host_crowdstrike(api_base, token, device_id):
|
|
"""Isolate a compromised host via CrowdStrike Falcon API."""
|
|
headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
|
|
resp = requests.post(
|
|
f"{api_base}/devices/entities/devices-actions/v2?action_name=contain",
|
|
headers=headers,
|
|
json={"ids": [device_id]},
|
|
timeout=30,
|
|
)
|
|
resp.raise_for_status()
|
|
logger.info("Host %s isolated via CrowdStrike", device_id)
|
|
return resp.json()
|
|
|
|
|
|
def search_iocs_splunk(splunk_url, session_key, ioc_list):
|
|
"""Search Splunk for IOC matches across the enterprise."""
|
|
ioc_query = " OR ".join([f'"{ioc}"' for ioc in ioc_list])
|
|
query = (
|
|
f"search index=sysmon (EventCode=1 OR EventCode=11 OR EventCode=3) ({ioc_query}) "
|
|
"| stats count by Computer, EventCode, Image, CommandLine | sort - count"
|
|
)
|
|
resp = requests.post(
|
|
f"{splunk_url}/services/search/jobs",
|
|
headers={"Authorization": f"Splunk {session_key}"},
|
|
data={"search": f"search {query}", "output_mode": "json"},
|
|
verify=not os.environ.get("SKIP_TLS_VERIFY", "").lower() == "true", # Set SKIP_TLS_VERIFY=true for self-signed certs in lab environments
|
|
timeout=30,
|
|
)
|
|
return resp.json()
|
|
|
|
|
|
def generate_ir_report(incident_id, variant, affected_hosts, containment_actions, iocs):
|
|
"""Generate a structured ransomware incident response report."""
|
|
report = {
|
|
"incident_id": incident_id,
|
|
"timestamp": datetime.utcnow().isoformat(),
|
|
"ransomware_variant": variant,
|
|
"affected_hosts": affected_hosts,
|
|
"containment_actions": containment_actions,
|
|
"indicators_of_compromise": iocs,
|
|
"phases": {
|
|
"detection": "Completed",
|
|
"containment": "Completed" if containment_actions else "Pending",
|
|
"eradication": "Pending",
|
|
"recovery": "Pending",
|
|
},
|
|
}
|
|
print(json.dumps(report, indent=2))
|
|
return report
|
|
|
|
|
|
def collect_iocs_from_sample(sample_path):
|
|
"""Extract IOCs from a ransomware sample file hash."""
|
|
with open(sample_path, "rb") as f:
|
|
content = f.read()
|
|
sha256 = hashlib.sha256(content).hexdigest()
|
|
md5 = hashlib.md5(content).hexdigest()
|
|
logger.info("Sample SHA-256: %s", sha256)
|
|
logger.info("Sample MD5: %s", md5)
|
|
|
|
bazaar_info = query_malwarebazaar_hash(sha256)
|
|
iocs = {"sha256": sha256, "md5": md5}
|
|
if bazaar_info:
|
|
iocs["family"] = bazaar_info.get("signature", "unknown")
|
|
iocs["tags"] = bazaar_info.get("tags", [])
|
|
return iocs
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Ransomware Playbook Automation Agent")
|
|
parser.add_argument("--incident-id", required=True, help="Incident ticket ID")
|
|
parser.add_argument("--sample", help="Path to encrypted file sample")
|
|
parser.add_argument("--device-id", help="CrowdStrike device ID for isolation")
|
|
parser.add_argument("--cs-token", help="CrowdStrike API bearer token")
|
|
parser.add_argument("--splunk-url", help="Splunk management URL")
|
|
parser.add_argument("--splunk-key", help="Splunk session key")
|
|
parser.add_argument("--output", default="ransomware_ir_report.json")
|
|
args = parser.parse_args()
|
|
|
|
iocs = {}
|
|
variant = "Unknown"
|
|
containment_actions = []
|
|
|
|
if args.sample:
|
|
iocs = collect_iocs_from_sample(args.sample)
|
|
variant = iocs.get("family", "Unknown")
|
|
query_nomoreransom(variant)
|
|
|
|
if args.device_id and args.cs_token:
|
|
isolate_host_crowdstrike(
|
|
"https://api.crowdstrike.com", args.cs_token, args.device_id
|
|
)
|
|
containment_actions.append(f"Isolated device {args.device_id} via CrowdStrike")
|
|
|
|
if args.splunk_url and args.splunk_key and iocs:
|
|
search_iocs_splunk(args.splunk_url, args.splunk_key, [iocs.get("sha256", "")])
|
|
|
|
report = generate_ir_report(
|
|
args.incident_id, variant, [args.device_id] if args.device_id else [], containment_actions, iocs
|
|
)
|
|
with open(args.output, "w") as f:
|
|
json.dump(report, f, indent=2)
|
|
logger.info("IR report saved to %s", args.output)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|