fix: wire prober tcpfp_fingerprint events into sniffer_rollup for OS/hop detection
The active prober emits tcpfp_fingerprint events with TTL, window, MSS etc. from the attacker's SYN-ACK. These were invisible to the behavioral profiler for two reasons: 1. target_ip (prober's field name for attacker IP) was not in _IP_FIELDS in collector/worker.py or correlation/parser.py, so the profiler re-parsed raw_lines and got attacker_ip=None, never attributing prober events to the attacker profile. 2. sniffer_rollup only handled tcp_syn_fingerprint (passive sniffer) and ignored tcpfp_fingerprint (active prober). Prober events use different field names: window_size/window_scale/sack_ok vs window/wscale/has_sack. Changes: - Add target_ip to _IP_FIELDS in collector and parser - Add _PROBER_TCPFP_EVENT and _INITIAL_TTL table to behavioral.py - sniffer_rollup now processes tcpfp_fingerprint: maps field names, derives OS from TTL via _os_from_ttl, computes hop_distance = initial_ttl - observed - Expand prober DEFAULT_TCPFP_PORTS to [22,80,443,8080,8443,445,3389] for better SYN-ACK coverage on attacker machines - Add 4 tests covering prober OS detection, hop distance, and field mapping
This commit is contained in:
@@ -423,6 +423,45 @@ class TestSnifferRollup:
|
||||
r = sniffer_rollup(events)
|
||||
assert r["os_guess"] == "macos_ios"
|
||||
|
||||
def test_prober_tcpfp_os_from_ttl(self):
|
||||
# Active-probe event: TTL=121 → windows OS guess.
|
||||
events = [
|
||||
_mk(0, event_type="tcpfp_fingerprint",
|
||||
fields={"ttl": "121", "window_size": "64240", "mss": "1460",
|
||||
"window_scale": "8", "sack_ok": "1", "timestamp": "0",
|
||||
"options_order": "M,N,W,N,N,S"}),
|
||||
]
|
||||
r = sniffer_rollup(events)
|
||||
assert r["os_guess"] == "windows"
|
||||
|
||||
def test_prober_tcpfp_hop_distance_derived(self):
|
||||
# TTL=121 with windows initial TTL=128 → hop_distance=7.
|
||||
events = [
|
||||
_mk(0, event_type="tcpfp_fingerprint",
|
||||
fields={"ttl": "121", "window_size": "64240", "mss": "1460",
|
||||
"window_scale": "8", "sack_ok": "1", "timestamp": "0",
|
||||
"options_order": "M,N,W,N,N,S"}),
|
||||
]
|
||||
r = sniffer_rollup(events)
|
||||
assert r["hop_distance"] == 7
|
||||
|
||||
def test_prober_tcpfp_tcp_fingerprint_fields(self):
|
||||
# Prober field names (window_size, window_scale, etc.) are mapped correctly.
|
||||
events = [
|
||||
_mk(0, event_type="tcpfp_fingerprint",
|
||||
fields={"ttl": "60", "window_size": "29200", "mss": "1460",
|
||||
"window_scale": "7", "sack_ok": "1", "timestamp": "1",
|
||||
"options_order": "M,N,W,N,N,T,S,E"}),
|
||||
]
|
||||
r = sniffer_rollup(events)
|
||||
fp = r["tcp_fingerprint"]
|
||||
assert fp["window"] == 29200
|
||||
assert fp["wscale"] == 7
|
||||
assert fp["mss"] == 1460
|
||||
assert fp["has_sack"] is True
|
||||
assert fp["has_timestamps"] is True
|
||||
assert fp["options_sig"] == "M,N,W,N,N,T,S,E"
|
||||
|
||||
|
||||
# ─── build_behavior_record (composite) ──────────────────────────────────────
|
||||
|
||||
|
||||
Reference in New Issue
Block a user