58 lines
2.1 KiB
Python
58 lines
2.1 KiB
Python
"""Coarse behavior classification for DECNET attacker profiles."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from decnet.telemetry import traced as _traced
|
|
|
|
|
|
@_traced("profiler.classify_behavior")
|
|
def classify_behavior(stats: dict[str, Any], services_count: int) -> str:
|
|
"""
|
|
Coarse behavior bucket:
|
|
beaconing | interactive | scanning | brute_force | slow_scan | mixed | unknown
|
|
|
|
Heuristics (evaluated in priority order):
|
|
* `scanning` — ≥ 3 services touched OR mean IAT < 2 s, ≥ 3 events
|
|
* `brute_force` — 1 service, n ≥ 8, mean IAT < 5 s, CV < 0.6
|
|
* `beaconing` — CV < 0.35, mean IAT ≥ 5 s, ≥ 4 events
|
|
* `slow_scan` — ≥ 2 services, mean IAT ≥ 10 s, ≥ 4 events
|
|
* `interactive` — mean IAT < 5 s AND CV ≥ 0.5, ≥ 6 events
|
|
* `mixed` — catch-all for sessions with enough data
|
|
* `unknown` — too few data points
|
|
"""
|
|
n = stats.get("event_count") or 0
|
|
mean = stats.get("mean_iat_s")
|
|
cv = stats.get("cv")
|
|
|
|
if n < 3 or mean is None:
|
|
return "unknown"
|
|
|
|
# Slow scan / low-and-slow: multiple services with long gaps.
|
|
# Must be checked before generic scanning so slow multi-service sessions
|
|
# don't get mis-bucketed as a fast sweep.
|
|
if services_count >= 2 and mean >= 10.0 and n >= 4:
|
|
return "slow_scan"
|
|
|
|
# Scanning: broad service sweep (multi-service) or very rapid single-service bursts.
|
|
if n >= 3 and (
|
|
(services_count >= 3 and mean < 10.0)
|
|
or (services_count >= 2 and mean < 2.0)
|
|
):
|
|
return "scanning"
|
|
|
|
# Brute force: hammering one service rapidly and repeatedly.
|
|
if services_count == 1 and n >= 8 and mean < 5.0 and cv is not None and cv < 0.6:
|
|
return "brute_force"
|
|
|
|
# Beaconing: regular cadence over multiple events.
|
|
if cv is not None and cv < 0.35 and mean >= 5.0 and n >= 4:
|
|
return "beaconing"
|
|
|
|
# Interactive: short but irregular bursts (human or tool with think time).
|
|
if cv is not None and cv >= 0.5 and mean < 5.0 and n >= 6:
|
|
return "interactive"
|
|
|
|
return "mixed"
|