fix: promote TCP-fingerprinted nmap to tool_guesses (detects -sC sans HTTP)
This commit is contained in:
@@ -504,6 +504,13 @@ def build_behavior_record(events: list[LogEvent]) -> dict[str, Any]:
|
||||
header_tools = detect_tools_from_headers(events)
|
||||
all_tools: list[str] = list(dict.fromkeys(beacon_tools + header_tools)) # dedup, preserve order
|
||||
|
||||
# Promote TCP-level scanner identification to tool_guesses.
|
||||
# p0f fingerprints nmap from the TCP handshake alone — this fires even
|
||||
# when no HTTP service is present, making it far more reliable than the
|
||||
# header-based path for raw port scans.
|
||||
if rollup["os_guess"] == "nmap" and "nmap" not in all_tools:
|
||||
all_tools.insert(0, "nmap")
|
||||
|
||||
# Beacon-specific projection: only surface interval/jitter when we've
|
||||
# classified the flow as beaconing (otherwise these numbers are noise).
|
||||
beacon_interval_s: float | None = None
|
||||
|
||||
@@ -475,3 +475,15 @@ class TestBuildBehaviorRecord:
|
||||
events = [_mk(i * 300.0) for i in range(5)] # 5-min intervals, no signature match
|
||||
r = build_behavior_record(events)
|
||||
assert json.loads(r["tool_guesses"]) == []
|
||||
|
||||
def test_nmap_promoted_from_tcp_fingerprint(self):
|
||||
# p0f identifies nmap from TCP handshake → must appear in tool_guesses
|
||||
# even when no HTTP request events are present.
|
||||
events = [
|
||||
_mk(0, event_type="tcp_syn_fingerprint", service="ssh",
|
||||
fields={"os_guess": "nmap", "window": "31337", "ttl": "58"}),
|
||||
_mk(1, event_type="tcp_syn_fingerprint", service="smb",
|
||||
fields={"os_guess": "nmap", "window": "31337", "ttl": "58"}),
|
||||
]
|
||||
r = build_behavior_record(events)
|
||||
assert "nmap" in json.loads(r["tool_guesses"])
|
||||
|
||||
Reference in New Issue
Block a user