mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-12 22:24:56 +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
178 lines
7.1 KiB
Python
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()
|