feat(sniffer): capture SSH client banner from TCP stream

Parse RFC 4253 §4.2 identification strings from the first attacker→decky
data segment on TCP/22; emit ssh_client_banner syslog events and bus
fan-out. Profiler's sniffer_rollup dedupes observed banners into a new
AttackerBehavior.ssh_client_banners JSON column.

Closes gap #3 from SIGNAL_CAPTURE_AUDIT.md.
This commit is contained in:
2026-04-22 21:37:01 -04:00
parent 8181f39ae2
commit d3321324eb
7 changed files with 148 additions and 0 deletions

View File

@@ -90,11 +90,13 @@ def build_behavior_record(events: list[LogEvent]) -> dict[str, Any]:
_span.set_attribute("tools", ",".join(all_tools))
kex_list = rollup.get("kex_order_raw") or []
ssh_banners = rollup.get("ssh_client_banners") or []
return {
"os_guess": rollup["os_guess"],
"hop_distance": rollup["hop_distance"],
"tcp_fingerprint": json.dumps(rollup["tcp_fingerprint"]),
"kex_order_raw": json.dumps(kex_list) if kex_list else None,
"ssh_client_banners": json.dumps(ssh_banners) if ssh_banners else None,
"retransmit_count": rollup["retransmit_count"],
"behavior_class": behavior,
"beacon_interval_s": beacon_interval_s,