mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-25 05:50:57 +03:00
Initial commit - 611 cybersecurity skills across all subdomains
This commit is contained in:
@@ -0,0 +1,314 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Elastic SIEM Alert Triage Automation
|
||||
|
||||
Provides alert triage scoring, classification assistance,
|
||||
and triage workflow management for Elastic Security alerts.
|
||||
"""
|
||||
|
||||
import json
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Optional
|
||||
|
||||
|
||||
SEVERITY_WEIGHTS = {
|
||||
"critical": 100,
|
||||
"high": 75,
|
||||
"medium": 50,
|
||||
"low": 25,
|
||||
"informational": 10,
|
||||
}
|
||||
|
||||
ASSET_CRITICALITY = {
|
||||
"critical": 1.5,
|
||||
"high": 1.3,
|
||||
"medium": 1.0,
|
||||
"low": 0.7,
|
||||
}
|
||||
|
||||
MITRE_TACTIC_PRIORITY = {
|
||||
"Impact": 95,
|
||||
"Exfiltration": 90,
|
||||
"Lateral Movement": 85,
|
||||
"Credential Access": 80,
|
||||
"Command and Control": 75,
|
||||
"Defense Evasion": 70,
|
||||
"Privilege Escalation": 65,
|
||||
"Persistence": 60,
|
||||
"Execution": 55,
|
||||
"Collection": 50,
|
||||
"Discovery": 40,
|
||||
"Initial Access": 35,
|
||||
"Reconnaissance": 20,
|
||||
"Resource Development": 15,
|
||||
}
|
||||
|
||||
|
||||
class TriageAlert:
|
||||
"""Represents a security alert for triage processing."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
alert_id: str,
|
||||
rule_name: str,
|
||||
severity: str,
|
||||
risk_score: int,
|
||||
host_name: str,
|
||||
user_name: str,
|
||||
source_ip: str,
|
||||
mitre_tactic: str,
|
||||
mitre_technique: str,
|
||||
timestamp: str,
|
||||
asset_criticality: str = "medium",
|
||||
related_alert_count: int = 0,
|
||||
threat_intel_match: bool = False,
|
||||
):
|
||||
self.alert_id = alert_id
|
||||
self.rule_name = rule_name
|
||||
self.severity = severity
|
||||
self.risk_score = risk_score
|
||||
self.host_name = host_name
|
||||
self.user_name = user_name
|
||||
self.source_ip = source_ip
|
||||
self.mitre_tactic = mitre_tactic
|
||||
self.mitre_technique = mitre_technique
|
||||
self.timestamp = timestamp
|
||||
self.asset_criticality = asset_criticality
|
||||
self.related_alert_count = related_alert_count
|
||||
self.threat_intel_match = threat_intel_match
|
||||
self.classification = None
|
||||
self.triage_notes = []
|
||||
self.triage_timestamp = None
|
||||
|
||||
def calculate_triage_priority(self) -> dict:
|
||||
"""Calculate composite triage priority score."""
|
||||
base_score = SEVERITY_WEIGHTS.get(self.severity, 50)
|
||||
asset_multiplier = ASSET_CRITICALITY.get(self.asset_criticality, 1.0)
|
||||
tactic_score = MITRE_TACTIC_PRIORITY.get(self.mitre_tactic, 50)
|
||||
|
||||
# Boost for related alerts (potential attack chain)
|
||||
chain_boost = min(self.related_alert_count * 5, 25)
|
||||
|
||||
# Boost for threat intelligence match
|
||||
ti_boost = 20 if self.threat_intel_match else 0
|
||||
|
||||
composite_score = min(100, (base_score * asset_multiplier * 0.4)
|
||||
+ (tactic_score * 0.3)
|
||||
+ chain_boost
|
||||
+ ti_boost)
|
||||
|
||||
if composite_score >= 85:
|
||||
priority = "P1-CRITICAL"
|
||||
sla = "15 minutes"
|
||||
elif composite_score >= 70:
|
||||
priority = "P2-HIGH"
|
||||
sla = "30 minutes"
|
||||
elif composite_score >= 50:
|
||||
priority = "P3-MEDIUM"
|
||||
sla = "4 hours"
|
||||
elif composite_score >= 30:
|
||||
priority = "P4-LOW"
|
||||
sla = "8 hours"
|
||||
else:
|
||||
priority = "P5-INFO"
|
||||
sla = "24 hours"
|
||||
|
||||
return {
|
||||
"composite_score": round(composite_score, 1),
|
||||
"priority": priority,
|
||||
"response_sla": sla,
|
||||
"score_breakdown": {
|
||||
"severity_component": round(base_score * asset_multiplier * 0.4, 1),
|
||||
"tactic_component": round(tactic_score * 0.3, 1),
|
||||
"chain_boost": chain_boost,
|
||||
"threat_intel_boost": ti_boost,
|
||||
},
|
||||
}
|
||||
|
||||
def classify(self, classification: str, notes: str):
|
||||
"""Classify the alert after triage."""
|
||||
valid_classifications = [
|
||||
"true_positive",
|
||||
"false_positive",
|
||||
"benign_true_positive",
|
||||
"needs_investigation",
|
||||
]
|
||||
if classification not in valid_classifications:
|
||||
raise ValueError(f"Invalid classification. Must be one of: {valid_classifications}")
|
||||
self.classification = classification
|
||||
self.triage_notes.append(notes)
|
||||
self.triage_timestamp = datetime.utcnow().isoformat()
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
priority = self.calculate_triage_priority()
|
||||
return {
|
||||
"alert_id": self.alert_id,
|
||||
"rule_name": self.rule_name,
|
||||
"severity": self.severity,
|
||||
"risk_score": self.risk_score,
|
||||
"host_name": self.host_name,
|
||||
"user_name": self.user_name,
|
||||
"source_ip": self.source_ip,
|
||||
"mitre_tactic": self.mitre_tactic,
|
||||
"mitre_technique": self.mitre_technique,
|
||||
"timestamp": self.timestamp,
|
||||
"triage_priority": priority,
|
||||
"classification": self.classification,
|
||||
"triage_notes": self.triage_notes,
|
||||
"triage_timestamp": self.triage_timestamp,
|
||||
}
|
||||
|
||||
|
||||
class TriageQueue:
|
||||
"""Manages a queue of alerts for SOC triage."""
|
||||
|
||||
def __init__(self):
|
||||
self.alerts = []
|
||||
self.triaged = []
|
||||
self.metrics = {
|
||||
"total_received": 0,
|
||||
"total_triaged": 0,
|
||||
"true_positives": 0,
|
||||
"false_positives": 0,
|
||||
"benign_true_positives": 0,
|
||||
"pending_investigation": 0,
|
||||
"triage_times": [],
|
||||
}
|
||||
|
||||
def add_alert(self, alert: TriageAlert):
|
||||
self.alerts.append(alert)
|
||||
self.metrics["total_received"] += 1
|
||||
|
||||
def get_prioritized_queue(self) -> list:
|
||||
"""Return alerts sorted by triage priority score (highest first)."""
|
||||
return sorted(
|
||||
self.alerts,
|
||||
key=lambda a: a.calculate_triage_priority()["composite_score"],
|
||||
reverse=True,
|
||||
)
|
||||
|
||||
def triage_alert(self, alert_id: str, classification: str, notes: str, triage_duration_seconds: int):
|
||||
for i, alert in enumerate(self.alerts):
|
||||
if alert.alert_id == alert_id:
|
||||
alert.classify(classification, notes)
|
||||
self.triaged.append(alert)
|
||||
self.alerts.pop(i)
|
||||
self.metrics["total_triaged"] += 1
|
||||
self.metrics["triage_times"].append(triage_duration_seconds)
|
||||
if classification == "true_positive":
|
||||
self.metrics["true_positives"] += 1
|
||||
elif classification == "false_positive":
|
||||
self.metrics["false_positives"] += 1
|
||||
elif classification == "benign_true_positive":
|
||||
self.metrics["benign_true_positives"] += 1
|
||||
elif classification == "needs_investigation":
|
||||
self.metrics["pending_investigation"] += 1
|
||||
return alert
|
||||
return None
|
||||
|
||||
def get_triage_metrics(self) -> dict:
|
||||
times = self.metrics["triage_times"]
|
||||
total = self.metrics["total_triaged"]
|
||||
return {
|
||||
"total_received": self.metrics["total_received"],
|
||||
"total_triaged": total,
|
||||
"pending": len(self.alerts),
|
||||
"true_positive_count": self.metrics["true_positives"],
|
||||
"false_positive_count": self.metrics["false_positives"],
|
||||
"false_positive_rate": round(self.metrics["false_positives"] / total * 100, 1) if total > 0 else 0,
|
||||
"mean_triage_time_seconds": round(sum(times) / len(times), 1) if times else 0,
|
||||
"max_triage_time_seconds": max(times) if times else 0,
|
||||
"min_triage_time_seconds": min(times) if times else 0,
|
||||
}
|
||||
|
||||
|
||||
def generate_esql_queries(alert: TriageAlert) -> dict:
|
||||
"""Generate ES|QL queries for investigating an alert."""
|
||||
return {
|
||||
"related_host_activity": (
|
||||
f'FROM logs-*\n'
|
||||
f'| WHERE host.name == "{alert.host_name}" '
|
||||
f'AND @timestamp > NOW() - 1 HOUR\n'
|
||||
f'| STATS count = COUNT(*) BY event.category, event.action\n'
|
||||
f'| SORT count DESC'
|
||||
),
|
||||
"user_activity": (
|
||||
f'FROM logs-*\n'
|
||||
f'| WHERE user.name == "{alert.user_name}" '
|
||||
f'AND @timestamp > NOW() - 24 HOURS\n'
|
||||
f'| STATS count = COUNT(*), '
|
||||
f'unique_hosts = COUNT_DISTINCT(host.name) BY event.category\n'
|
||||
f'| SORT count DESC'
|
||||
),
|
||||
"source_ip_alerts": (
|
||||
f'FROM .alerts-security.alerts-default\n'
|
||||
f'| WHERE source.ip == "{alert.source_ip}" '
|
||||
f'AND @timestamp > NOW() - 24 HOURS\n'
|
||||
f'| STATS alert_count = COUNT(*) '
|
||||
f'BY kibana.alert.rule.name, kibana.alert.severity\n'
|
||||
f'| SORT alert_count DESC'
|
||||
),
|
||||
"network_connections": (
|
||||
f'FROM logs-endpoint.events.network-*\n'
|
||||
f'| WHERE host.name == "{alert.host_name}" '
|
||||
f'AND @timestamp > NOW() - 1 HOUR\n'
|
||||
f'| STATS conn_count = COUNT(*) BY destination.ip, destination.port\n'
|
||||
f'| SORT conn_count DESC\n'
|
||||
f'| LIMIT 20'
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
queue = TriageQueue()
|
||||
|
||||
sample_alerts = [
|
||||
TriageAlert("ALT-001", "Multiple Failed Logins Followed by Success", "high", 73,
|
||||
"DC01", "admin", "10.0.0.50", "Credential Access", "T1110",
|
||||
"2025-01-15T10:30:00Z", "critical", 3, False),
|
||||
TriageAlert("ALT-002", "Suspicious PowerShell Execution", "critical", 91,
|
||||
"WS-042", "jsmith", "10.0.1.42", "Execution", "T1059.001",
|
||||
"2025-01-15T10:32:00Z", "high", 5, True),
|
||||
TriageAlert("ALT-003", "Unusual DNS Query Volume", "medium", 47,
|
||||
"WS-108", "mwilson", "10.0.2.108", "Command and Control", "T1071",
|
||||
"2025-01-15T10:35:00Z", "medium", 0, False),
|
||||
TriageAlert("ALT-004", "New Windows Service Created", "low", 21,
|
||||
"SRV-DB01", "svc-deploy", "10.0.3.10", "Persistence", "T1543.003",
|
||||
"2025-01-15T10:37:00Z", "low", 0, False),
|
||||
]
|
||||
|
||||
for alert in sample_alerts:
|
||||
queue.add_alert(alert)
|
||||
|
||||
print("=" * 70)
|
||||
print("ELASTIC SIEM ALERT TRIAGE QUEUE")
|
||||
print("=" * 70)
|
||||
|
||||
prioritized = queue.get_prioritized_queue()
|
||||
for i, alert in enumerate(prioritized, 1):
|
||||
p = alert.calculate_triage_priority()
|
||||
print(f"\n{i}. [{p['priority']}] {alert.rule_name}")
|
||||
print(f" Score: {p['composite_score']} | SLA: {p['response_sla']}")
|
||||
print(f" Host: {alert.host_name} | User: {alert.user_name} | Tactic: {alert.mitre_tactic}")
|
||||
|
||||
# Simulate triage
|
||||
queue.triage_alert("ALT-002", "true_positive", "Confirmed Mimikatz execution via encoded PS", 180)
|
||||
queue.triage_alert("ALT-001", "true_positive", "Brute force followed by successful admin login", 300)
|
||||
queue.triage_alert("ALT-003", "false_positive", "DNS queries to known CDN - normal behavior", 120)
|
||||
queue.triage_alert("ALT-004", "benign_true_positive", "SCCM deployment creating expected service", 90)
|
||||
|
||||
print(f"\n{'=' * 70}")
|
||||
print("TRIAGE METRICS")
|
||||
print("=" * 70)
|
||||
metrics = queue.get_triage_metrics()
|
||||
for key, value in metrics.items():
|
||||
print(f" {key}: {value}")
|
||||
|
||||
# Show investigation queries
|
||||
print(f"\n{'=' * 70}")
|
||||
print("ES|QL INVESTIGATION QUERIES FOR ALT-002")
|
||||
print("=" * 70)
|
||||
queries = generate_esql_queries(sample_alerts[1])
|
||||
for name, query in queries.items():
|
||||
print(f"\n--- {name} ---")
|
||||
print(query)
|
||||
Reference in New Issue
Block a user