mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-11 05:34:55 +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
263 lines
10 KiB
Python
263 lines
10 KiB
Python
#!/usr/bin/env python3
|
|
"""Agent for triaging security incidents using NIST SP 800-61 and SANS PICERL frameworks."""
|
|
|
|
import requests
|
|
import json
|
|
import argparse
|
|
from datetime import datetime, timezone
|
|
|
|
|
|
NIST_CATEGORIES = {
|
|
"unauthorized_access": "Unauthorized Access",
|
|
"dos": "Denial of Service",
|
|
"malicious_code": "Malicious Code",
|
|
"improper_usage": "Improper Usage",
|
|
"reconnaissance": "Reconnaissance",
|
|
"web_attack": "Web Application Attack",
|
|
}
|
|
|
|
SEVERITY_MATRIX = {
|
|
"P1": {"label": "Critical", "ack_sla": "15 min", "contain_sla": "1 hour",
|
|
"criteria": "Crown jewel compromise, active exfiltration, ransomware spreading"},
|
|
"P2": {"label": "High", "ack_sla": "30 min", "contain_sla": "4 hours",
|
|
"criteria": "Production compromise, confirmed malware, privileged account takeover"},
|
|
"P3": {"label": "Medium", "ack_sla": "2 hours", "contain_sla": "24 hours",
|
|
"criteria": "Non-production compromise, failed exploitation, single endpoint malware"},
|
|
"P4": {"label": "Low", "ack_sla": "8 hours", "contain_sla": "72 hours",
|
|
"criteria": "Reconnaissance, policy violation, benign true positive"},
|
|
}
|
|
|
|
|
|
def classify_incident(alert_data):
|
|
"""Classify incident type based on alert indicators."""
|
|
print("[*] Classifying incident type...")
|
|
alert_name = alert_data.get("alert_name", "").lower()
|
|
process = alert_data.get("process", "").lower()
|
|
event_code = alert_data.get("event_code", "")
|
|
|
|
if any(kw in alert_name for kw in ["malware", "ransomware", "trojan", "cryptominer"]):
|
|
category = "malicious_code"
|
|
elif any(kw in alert_name for kw in ["brute force", "credential", "password spray"]):
|
|
category = "unauthorized_access"
|
|
elif any(kw in alert_name for kw in ["dos", "flood", "resource exhaustion"]):
|
|
category = "dos"
|
|
elif any(kw in alert_name for kw in ["sql injection", "xss", "ssrf", "xxe"]):
|
|
category = "web_attack"
|
|
elif any(kw in alert_name for kw in ["scan", "enum", "recon", "discovery"]):
|
|
category = "reconnaissance"
|
|
elif "powershell" in process and "encoded" in alert_name.lower():
|
|
category = "malicious_code"
|
|
else:
|
|
category = "unauthorized_access"
|
|
|
|
classification = NIST_CATEGORIES.get(category, "Unknown")
|
|
print(f" [+] Classification: {classification} ({category})")
|
|
return category, classification
|
|
|
|
|
|
def assess_severity(alert_data, asset_criticality="medium"):
|
|
"""Calculate incident severity based on threat and asset context."""
|
|
print("\n[*] Assessing severity...")
|
|
threat_score = 0
|
|
|
|
alert_severity = alert_data.get("severity", "").lower()
|
|
if alert_severity in ("critical", "high"):
|
|
threat_score += 3
|
|
elif alert_severity == "medium":
|
|
threat_score += 2
|
|
else:
|
|
threat_score += 1
|
|
|
|
confidence = alert_data.get("confidence", 50)
|
|
if confidence >= 80:
|
|
threat_score += 2
|
|
elif confidence >= 50:
|
|
threat_score += 1
|
|
|
|
asset_scores = {"critical": 3, "high": 2, "medium": 1, "low": 0}
|
|
asset_score = asset_scores.get(asset_criticality, 1)
|
|
|
|
total = threat_score + asset_score
|
|
if total >= 7:
|
|
priority = "P1"
|
|
elif total >= 5:
|
|
priority = "P2"
|
|
elif total >= 3:
|
|
priority = "P3"
|
|
else:
|
|
priority = "P4"
|
|
|
|
sev_info = SEVERITY_MATRIX[priority]
|
|
print(f" [+] Priority: {priority} - {sev_info['label']}")
|
|
print(f" [+] ACK SLA: {sev_info['ack_sla']} | Containment SLA: {sev_info['contain_sla']}")
|
|
return priority, sev_info
|
|
|
|
|
|
def check_virustotal(api_key, indicator, indicator_type="ip"):
|
|
"""Check an indicator against VirusTotal."""
|
|
print(f"\n[*] Checking VirusTotal for {indicator_type}: {indicator}...")
|
|
base_urls = {
|
|
"ip": f"https://www.virustotal.com/api/v3/ip_addresses/{indicator}",
|
|
"hash": f"https://www.virustotal.com/api/v3/files/{indicator}",
|
|
"domain": f"https://www.virustotal.com/api/v3/domains/{indicator}",
|
|
}
|
|
url = base_urls.get(indicator_type)
|
|
if not url:
|
|
return {}
|
|
try:
|
|
headers = {"x-apikey": api_key}
|
|
resp = requests.get(url, headers=headers, timeout=15)
|
|
if resp.status_code == 200:
|
|
data = resp.json().get("data", {}).get("attributes", {})
|
|
if indicator_type in ("ip", "domain"):
|
|
malicious = data.get("last_analysis_stats", {}).get("malicious", 0)
|
|
total = sum(data.get("last_analysis_stats", {}).values())
|
|
print(f" [+] VT Result: {malicious}/{total} vendors flagged as malicious")
|
|
return {"malicious": malicious, "total": total}
|
|
elif indicator_type == "hash":
|
|
malicious = data.get("last_analysis_stats", {}).get("malicious", 0)
|
|
name = data.get("meaningful_name", "Unknown")
|
|
print(f" [+] VT Result: {name} - {malicious} detections")
|
|
return {"name": name, "malicious": malicious}
|
|
elif resp.status_code == 404:
|
|
print(f" [-] Not found in VirusTotal")
|
|
else:
|
|
print(f" [-] VT API error: {resp.status_code}")
|
|
except requests.RequestException as e:
|
|
print(f" [-] VT request failed: {e}")
|
|
return {}
|
|
|
|
|
|
def build_mitre_mapping(category, process_info=""):
|
|
"""Map incident to MITRE ATT&CK techniques."""
|
|
mappings = {
|
|
"malicious_code": [
|
|
{"technique": "T1059.001", "name": "PowerShell"},
|
|
{"technique": "T1204.002", "name": "User Execution: Malicious File"},
|
|
],
|
|
"unauthorized_access": [
|
|
{"technique": "T1110", "name": "Brute Force"},
|
|
{"technique": "T1078", "name": "Valid Accounts"},
|
|
],
|
|
"reconnaissance": [
|
|
{"technique": "T1046", "name": "Network Service Discovery"},
|
|
{"technique": "T1595", "name": "Active Scanning"},
|
|
],
|
|
"web_attack": [
|
|
{"technique": "T1190", "name": "Exploit Public-Facing Application"},
|
|
],
|
|
}
|
|
techniques = mappings.get(category, [])
|
|
if techniques:
|
|
print(f"\n[*] MITRE ATT&CK mapping:")
|
|
for t in techniques:
|
|
print(f" - {t['technique']}: {t['name']}")
|
|
return techniques
|
|
|
|
|
|
def generate_triage_record(alert_data, classification, priority, sev_info,
|
|
ti_results, mitre, output_path):
|
|
"""Generate a structured incident triage report."""
|
|
triage_time = datetime.now(timezone.utc)
|
|
alert_time = alert_data.get("timestamp", triage_time.isoformat())
|
|
|
|
record = {
|
|
"ticket_id": f"INC-{triage_time.strftime('%Y')}-{hash(str(alert_data)) % 10000:04d}",
|
|
"triage_analyst": alert_data.get("analyst", "automated"),
|
|
"triage_time": triage_time.isoformat(),
|
|
"alert_time": alert_time,
|
|
"classification": {
|
|
"type": classification[1],
|
|
"category": classification[0],
|
|
"priority": priority,
|
|
"severity_label": sev_info["label"],
|
|
"confidence": alert_data.get("confidence", "Unknown"),
|
|
},
|
|
"affected_scope": {
|
|
"assets": alert_data.get("affected_hosts", []),
|
|
"users": alert_data.get("affected_users", []),
|
|
"business_unit": alert_data.get("business_unit", "Unknown"),
|
|
},
|
|
"evidence": {
|
|
"alert_source": alert_data.get("source", "Unknown"),
|
|
"alert_name": alert_data.get("alert_name", "Unknown"),
|
|
"raw_indicators": alert_data.get("indicators", {}),
|
|
},
|
|
"enrichment": {
|
|
"threat_intel": ti_results,
|
|
"mitre_attack": mitre,
|
|
},
|
|
"recommended_actions": [],
|
|
"sla": {
|
|
"acknowledge_by": sev_info["ack_sla"],
|
|
"contain_by": sev_info["contain_sla"],
|
|
},
|
|
}
|
|
|
|
if priority in ("P1", "P2"):
|
|
record["recommended_actions"] = [
|
|
"Isolate affected endpoint via EDR",
|
|
"Disable compromised user account",
|
|
"Preserve volatile evidence (memory dump)",
|
|
"Escalate to Tier 2 IR team",
|
|
]
|
|
else:
|
|
record["recommended_actions"] = [
|
|
"Monitor for additional indicators",
|
|
"Review related alerts for the past 7 days",
|
|
"Document findings and close if benign",
|
|
]
|
|
|
|
with open(output_path, "w") as f:
|
|
json.dump(record, f, indent=2)
|
|
print(f"\n[*] Triage record saved to {output_path}")
|
|
print(f"[*] Ticket: {record['ticket_id']} | Priority: {priority} ({sev_info['label']})")
|
|
return record
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Security Incident Triage Agent")
|
|
parser.add_argument("--alert-name", required=True, help="Name of the triggering alert")
|
|
parser.add_argument("--source", default="SIEM", help="Alert source system")
|
|
parser.add_argument("--severity", default="high", help="Alert severity")
|
|
parser.add_argument("--confidence", type=int, default=75, help="Alert confidence (0-100)")
|
|
parser.add_argument("--host", help="Affected hostname")
|
|
parser.add_argument("--src-ip", help="Source IP address")
|
|
parser.add_argument("--user", help="Affected username")
|
|
parser.add_argument("--process", default="", help="Suspicious process name")
|
|
parser.add_argument("--asset-criticality", default="medium",
|
|
choices=["critical", "high", "medium", "low"])
|
|
parser.add_argument("--vt-key", help="VirusTotal API key for threat intel")
|
|
parser.add_argument("--indicator", help="IOC to check (IP, hash, or domain)")
|
|
parser.add_argument("--indicator-type", default="ip", choices=["ip", "hash", "domain"])
|
|
parser.add_argument("-o", "--output", default="triage_record.json")
|
|
args = parser.parse_args()
|
|
|
|
alert_data = {
|
|
"alert_name": args.alert_name,
|
|
"source": args.source,
|
|
"severity": args.severity,
|
|
"confidence": args.confidence,
|
|
"affected_hosts": [args.host] if args.host else [],
|
|
"affected_users": [args.user] if args.user else [],
|
|
"process": args.process,
|
|
"indicators": {"src_ip": args.src_ip},
|
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
|
}
|
|
|
|
print("[*] Security Incident Triage\n")
|
|
classification = classify_incident(alert_data)
|
|
priority, sev_info = assess_severity(alert_data, args.asset_criticality)
|
|
mitre = build_mitre_mapping(classification[0], args.process)
|
|
|
|
ti_results = {}
|
|
if args.vt_key and args.indicator:
|
|
ti_results = check_virustotal(args.vt_key, args.indicator, args.indicator_type)
|
|
|
|
generate_triage_record(alert_data, classification, priority, sev_info,
|
|
ti_results, mitre, args.output)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|