66 lines
2 KiB
Python
66 lines
2 KiB
Python
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
"""Read-only incident correlation rules.
|
|
|
|
Correlation does not hide the raw alerts or mutate the host. It adds a small
|
|
``correlations`` list to an incident when the incident's existing evidence
|
|
matches a higher-confidence story.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
|
|
from .alert import Severity
|
|
|
|
SID_MULTI_STAGE_INTRUSION = 100080
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class CorrelationRule:
|
|
sid: int
|
|
signature: str
|
|
classtype: str
|
|
severity: Severity
|
|
summary: str
|
|
required_any: tuple[set[str], ...]
|
|
max_window: int
|
|
|
|
|
|
DEFAULT_RULES: tuple[CorrelationRule, ...] = (
|
|
CorrelationRule(
|
|
sid=SID_MULTI_STAGE_INTRUSION,
|
|
signature="correlation.multi_stage_intrusion",
|
|
classtype="multi-stage-intrusion",
|
|
severity=Severity.CRITICAL,
|
|
summary=(
|
|
"Web/database service spawned a shell and the same incident showed "
|
|
"suspicious egress or an unusual listener within the correlation window"
|
|
),
|
|
required_any=(
|
|
{"exec_rule.web-rce"},
|
|
{"host_rule.suspicious-egress", "host_rule.suspicious-listener"},
|
|
),
|
|
max_window=600,
|
|
),
|
|
)
|
|
|
|
|
|
def correlate(incident: dict, rules: tuple[CorrelationRule, ...] = DEFAULT_RULES) -> list[dict]:
|
|
"""Return correlation records matched by an incident index entry."""
|
|
signatures = set(incident.get("signatures") or [])
|
|
duration = float(incident.get("last_ts", 0.0)) - float(incident.get("first_ts", 0.0))
|
|
out: list[dict] = []
|
|
for rule in rules:
|
|
if duration > rule.max_window:
|
|
continue
|
|
if not all(signatures & choices for choices in rule.required_any):
|
|
continue
|
|
out.append({
|
|
"sid": rule.sid,
|
|
"signature": rule.signature,
|
|
"classtype": rule.classtype,
|
|
"severity": str(rule.severity),
|
|
"summary": rule.summary,
|
|
"window_seconds": rule.max_window,
|
|
"matched_signatures": sorted(signatures),
|
|
})
|
|
return out
|