#!/usr/bin/env python3 """Malware Incident Response Agent - Triages malware infections and automates containment.""" import json import hashlib import logging import os import argparse from datetime import datetime import requests logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s") logger = logging.getLogger(__name__) def hash_file(file_path): """Compute SHA-256 and MD5 hashes of a file.""" sha256 = hashlib.sha256() md5 = hashlib.md5() with open(file_path, "rb") as f: for chunk in iter(lambda: f.read(8192), b""): sha256.update(chunk) md5.update(chunk) return {"sha256": sha256.hexdigest(), "md5": md5.hexdigest()} def query_virustotal(file_hash, api_key): """Query VirusTotal for file hash reputation.""" url = f"https://www.virustotal.com/api/v3/files/{file_hash}" headers = {"x-apikey": api_key} resp = requests.get(url, headers=headers, timeout=30) if resp.status_code == 200: attrs = resp.json()["data"]["attributes"] stats = attrs.get("last_analysis_stats", {}) result = { "hash": file_hash, "malicious": stats.get("malicious", 0), "undetected": stats.get("undetected", 0), "total": sum(stats.values()), "popular_threat_name": attrs.get("popular_threat_classification", {}).get("suggested_threat_label", ""), "type_description": attrs.get("type_description", ""), "first_seen": attrs.get("first_submission_date"), "tags": attrs.get("tags", []), } logger.info("VT: %s - %d/%d detections (%s)", file_hash[:16], result["malicious"], result["total"], result["popular_threat_name"]) return result return {"hash": file_hash, "error": resp.status_code} def query_malwarebazaar(file_hash): """Query MalwareBazaar for sample metadata.""" url = "https://mb-api.abuse.ch/api/v1/" resp = requests.post(url, data={"query": "get_info", "hash": file_hash}, timeout=30) result = resp.json() if result.get("query_status") == "ok": sample = result["data"][0] return { "hash": file_hash, "family": sample.get("signature", "unknown"), "file_type": sample.get("file_type", "unknown"), "delivery_method": sample.get("delivery_method", "unknown"), "tags": sample.get("tags", []), "intelligence": sample.get("intelligence", {}), } return {"hash": file_hash, "status": "not_found"} def query_threatfox_iocs(malware_family): """Search ThreatFox for IOCs associated with a malware family.""" url = "https://threatfox-api.abuse.ch/api/v1/" data = {"query": "taginfo", "tag": malware_family, "limit": 50} resp = requests.post(url, json=data, timeout=30) result = resp.json() iocs = [] if result.get("query_status") == "ok": for entry in result.get("data", []): iocs.append({ "ioc": entry.get("ioc"), "ioc_type": entry.get("ioc_type"), "threat_type": entry.get("threat_type"), "malware": entry.get("malware_printable"), "confidence": entry.get("confidence_level"), }) logger.info("ThreatFox: %d IOCs for family '%s'", len(iocs), malware_family) return iocs def isolate_endpoint_crowdstrike(api_base, token, device_id): """Isolate an infected endpoint 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, ) if resp.status_code == 202: logger.info("Endpoint %s isolated via CrowdStrike", device_id) return True logger.error("Isolation failed for %s: %d", device_id, resp.status_code) return False def search_enterprise_iocs(splunk_url, session_key, iocs): """Search Splunk for IOC matches across the enterprise.""" ioc_values = [i.get("ioc", "") for i in iocs if i.get("ioc")] if not ioc_values: return [] search_terms = " OR ".join([f'"{v}"' for v in ioc_values[:20]]) query = f'search index=* ({search_terms}) | stats count by host, sourcetype | sort - count' resp = requests.post( f"{splunk_url}/services/search/jobs", headers={"Authorization": f"Splunk {session_key}"}, data={"search": query, "output_mode": "json"}, verify=not os.environ.get("SKIP_TLS_VERIFY", "").lower() == "true", timeout=30, # Set SKIP_TLS_VERIFY=true for self-signed certs in lab environments ) return resp.json() if resp.status_code == 201 else [] def generate_malware_ir_report(incident_id, hashes, vt_results, bazaar_results, family_iocs, containment): """Generate a malware incident response report.""" report = { "incident_id": incident_id, "timestamp": datetime.utcnow().isoformat(), "sample_hashes": hashes, "virustotal": vt_results, "malwarebazaar": bazaar_results, "malware_family": bazaar_results.get("family", vt_results.get("popular_threat_name", "Unknown")), "related_iocs": family_iocs[:20], "containment_actions": containment, } family = report["malware_family"] detections = vt_results.get("malicious", 0) print(f"MALWARE IR REPORT - {incident_id}") print(f"Family: {family} | VT Detections: {detections} | Related IOCs: {len(family_iocs)}") return report def main(): parser = argparse.ArgumentParser(description="Malware Incident Response Agent") parser.add_argument("--incident-id", required=True, help="Incident ID") parser.add_argument("--sample", help="Path to malware sample file") parser.add_argument("--hash", help="SHA-256 hash of malware sample") parser.add_argument("--vt-key", help="VirusTotal API key") parser.add_argument("--cs-token", help="CrowdStrike API token") parser.add_argument("--device-id", help="CrowdStrike device ID to isolate") parser.add_argument("--output", default="malware_ir_report.json") args = parser.parse_args() hashes = {} if args.sample: hashes = hash_file(args.sample) elif args.hash: hashes = {"sha256": args.hash} file_hash = hashes.get("sha256", "") vt_results = query_virustotal(file_hash, args.vt_key) if args.vt_key and file_hash else {} bazaar_results = query_malwarebazaar(file_hash) if file_hash else {} family = bazaar_results.get("family", vt_results.get("popular_threat_name", "")) family_iocs = query_threatfox_iocs(family) if family and family != "unknown" else [] containment = [] if args.cs_token and args.device_id: if isolate_endpoint_crowdstrike("https://api.crowdstrike.com", args.cs_token, args.device_id): containment.append(f"Isolated {args.device_id}") report = generate_malware_ir_report( args.incident_id, hashes, vt_results, bazaar_results, family_iocs, containment ) with open(args.output, "w") as f: json.dump(report, f, indent=2) logger.info("Report saved to %s", args.output) if __name__ == "__main__": main()