Files
Anthropic-Cybersecurity-Skills/skills/conducting-malware-incident-response/scripts/agent.py
T
mukul975 c47eed6a64 Production hardening: security fixes, code quality, 724 skills complete
- 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
2026-03-19 13:26:49 +01:00

178 lines
7.1 KiB
Python

#!/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()