feat: overhaul behavioral profiler — multi-tool detection, improved classification, TTL OS fallback

This commit is contained in:
2026-04-15 15:47:02 -04:00
parent 935a9a58d2
commit c8f05df4d9
7 changed files with 472 additions and 73 deletions

View File

@@ -117,10 +117,10 @@ class AttackerBehavior(SQLModel, table=True):
) # JSON: window, wscale, mss, options_sig
retransmit_count: int = Field(default=0)
# Behavioral (derived by the profiler from log-event timing)
behavior_class: Optional[str] = None # beaconing | interactive | scanning | mixed | unknown
behavior_class: Optional[str] = None # beaconing | interactive | scanning | brute_force | slow_scan | mixed | unknown
beacon_interval_s: Optional[float] = None
beacon_jitter_pct: Optional[float] = None
tool_guess: Optional[str] = None # cobalt_strike | sliver | havoc | mythic
tool_guesses: Optional[str] = None # JSON list[str] — all matched tools
timing_stats: str = Field(
default="{}",
sa_column=Column("timing_stats", Text, nullable=False, default="{}"),

View File

@@ -524,6 +524,16 @@ class SQLModelRepository(BaseRepository):
d[key] = json.loads(d[key])
except (json.JSONDecodeError, TypeError):
pass
# Deserialize tool_guesses JSON array; normalise None → [].
raw = d.get("tool_guesses")
if isinstance(raw, str):
try:
parsed = json.loads(raw)
d["tool_guesses"] = parsed if isinstance(parsed, list) else [parsed]
except (json.JSONDecodeError, TypeError):
d["tool_guesses"] = []
elif raw is None:
d["tool_guesses"] = []
return d
@staticmethod