Skip to content

Commit 565a6db

Browse files
committed
fix endpoint prober performance: pre-filter closed ports, add wazuh rules
Endpoint prober now does a quick TCP connect check (1.5s) on all 11 probe ports before firing HTTP requests. Closed ports are skipped entirely, reducing probes from ~1034 to only open ports. Also adds 2s connect timeout to prevent hanging on firewalled ports. Adds example Wazuh rules for AgentSniff log ingestion (syslog and JSON).
1 parent d7fa066 commit 565a6db

2 files changed

Lines changed: 243 additions & 2 deletions

File tree

agentsniff/detectors/endpoint_prober.py

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,17 +74,42 @@ async def scan(self, targets: list[str]) -> list[DetectionSignal]:
7474
f"({len(signatures)} frameworks, ~{total_probes} probes)..."
7575
)
7676

77-
timeout = aiohttp.ClientTimeout(total=self.config.http_timeout)
77+
timeout = aiohttp.ClientTimeout(
78+
total=self.config.http_timeout,
79+
connect=min(self.config.http_timeout, 2.0),
80+
sock_connect=min(self.config.http_timeout, 2.0),
81+
)
7882
connector = aiohttp.TCPConnector(
7983
ssl=False, limit=self.config.http_concurrency
8084
)
8185

86+
# Pre-filter: quick TCP connect to skip closed ports
87+
open_ports: dict[str, set[int]] = {}
88+
port_check_tasks = []
89+
for host in targets:
90+
for port in HTTP_PROBE_PORTS:
91+
port_check_tasks.append(self._check_port(host, port))
92+
port_results = await asyncio.gather(*port_check_tasks, return_exceptions=True)
93+
idx = 0
94+
for host in targets:
95+
for port in HTTP_PROBE_PORTS:
96+
result = port_results[idx]
97+
if result is True:
98+
open_ports.setdefault(host, set()).add(port)
99+
idx += 1
100+
101+
reachable_count = sum(len(ports) for ports in open_ports.values())
102+
self.logger.info(
103+
f"Found {reachable_count} open port(s) across {len(open_ports)} host(s), "
104+
f"skipping {len(targets) * len(HTTP_PROBE_PORTS) - reachable_count} closed"
105+
)
106+
82107
async with aiohttp.ClientSession(
83108
timeout=timeout, connector=connector
84109
) as session:
85110
tasks = []
86111
for host in targets:
87-
for port in HTTP_PROBE_PORTS:
112+
for port in open_ports.get(host, set()):
88113
# Framework-specific endpoint probing
89114
for fw_name, fw_sig in signatures.items():
90115
for path in fw_sig.get("endpoints", []):
@@ -131,6 +156,19 @@ async def scan(self, targets: list[str]) -> list[DetectionSignal]:
131156
)
132157
return signals
133158

159+
@staticmethod
160+
async def _check_port(host: str, port: int) -> bool:
161+
"""Quick TCP connect to check if port is open."""
162+
try:
163+
_, writer = await asyncio.wait_for(
164+
asyncio.open_connection(host, port), timeout=1.5
165+
)
166+
writer.close()
167+
await writer.wait_closed()
168+
return True
169+
except (OSError, asyncio.TimeoutError):
170+
return False
171+
134172
async def _probe_framework_endpoint(
135173
self,
136174
session: aiohttp.ClientSession,

docs/wazuh-rules.xml

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
<!--
2+
AgentSniff Wazuh Rules
3+
══════════════════════════════════════════════════════════════
4+
Drop these rules into /var/ossec/etc/rules/agentsniff_rules.xml
5+
and configure a log collector for the AgentSniff log file.
6+
7+
Wazuh agent ossec.conf snippet:
8+
─────────────────────────────────────────────────────────────
9+
<localfile>
10+
<log_format>syslog</log_format>
11+
<location>/var/log/agentsniff/scan.log</location>
12+
</localfile>
13+
─────────────────────────────────────────────────────────────
14+
15+
For JSON output (recommended for richer alerts), pipe scans to a
16+
JSON log and use json decoder:
17+
─────────────────────────────────────────────────────────────
18+
<localfile>
19+
<log_format>json</log_format>
20+
<location>/var/log/agentsniff/results.json</location>
21+
</localfile>
22+
─────────────────────────────────────────────────────────────
23+
24+
Rule ID range: 100200–100299 (custom rules)
25+
-->
26+
27+
<group name="agentsniff,">
28+
29+
<!-- ── Base rule: any AgentSniff log line ─────────────────────── -->
30+
<rule id="100200" level="0">
31+
<decoded_as>agentsniff</decoded_as>
32+
<description>AgentSniff log event.</description>
33+
</rule>
34+
35+
<!-- Also match by program name in syslog format -->
36+
<rule id="100201" level="0">
37+
<program_name>agentsniff</program_name>
38+
<description>AgentSniff log event (syslog).</description>
39+
</rule>
40+
41+
<!-- ── Scan lifecycle ─────────────────────────────────────────── -->
42+
<rule id="100210" level="3">
43+
<if_group>agentsniff</if_group>
44+
<match>Scan complete:</match>
45+
<description>AgentSniff scan completed.</description>
46+
</rule>
47+
48+
<rule id="100211" level="5">
49+
<if_group>agentsniff</if_group>
50+
<match>No valid targets resolved</match>
51+
<description>AgentSniff scan failed — no valid targets.</description>
52+
</rule>
53+
54+
<rule id="100212" level="4">
55+
<if_group>agentsniff</if_group>
56+
<match>No detectors enabled</match>
57+
<description>AgentSniff scan failed — no detectors enabled.</description>
58+
</rule>
59+
60+
<rule id="100213" level="3">
61+
<if_group>agentsniff</if_group>
62+
<match>Detector|setup failed</match>
63+
<description>AgentSniff detector setup failed.</description>
64+
</rule>
65+
66+
<!-- ── Agent detection (syslog format) ────────────────────────── -->
67+
<rule id="100220" level="7">
68+
<if_group>agentsniff</if_group>
69+
<match>agents detected</match>
70+
<description>AgentSniff detected AI agents on the network.</description>
71+
</rule>
72+
73+
<!-- ── Anomaly detection (continuous mode) ────────────────────── -->
74+
<rule id="100221" level="8">
75+
<if_group>agentsniff</if_group>
76+
<match>anomaly|anomalies</match>
77+
<description>AgentSniff baseline anomaly detected — new or changed agents.</description>
78+
</rule>
79+
80+
<!-- ── Alert notifications ────────────────────────────────────── -->
81+
<rule id="100230" level="3">
82+
<if_group>agentsniff</if_group>
83+
<match>Webhook alert sent</match>
84+
<description>AgentSniff webhook alert dispatched.</description>
85+
</rule>
86+
87+
<rule id="100231" level="5">
88+
<if_group>agentsniff</if_group>
89+
<match>Webhook returned|Webhook alert failed</match>
90+
<description>AgentSniff webhook alert delivery failed.</description>
91+
</rule>
92+
93+
<rule id="100232" level="3">
94+
<if_group>agentsniff</if_group>
95+
<match>Email alert sent</match>
96+
<description>AgentSniff email alert dispatched.</description>
97+
</rule>
98+
99+
<rule id="100233" level="5">
100+
<if_group>agentsniff</if_group>
101+
<match>Email alert failed</match>
102+
<description>AgentSniff email alert delivery failed.</description>
103+
</rule>
104+
105+
<!-- ═══════════════════════════════════════════════════════════
106+
JSON-based rules (for --format json output)
107+
Use with <log_format>json</log_format>
108+
═══════════════════════════════════════════════════════════ -->
109+
110+
<!-- ── Any agent detected in JSON results ─────────────────────── -->
111+
<rule id="100240" level="7">
112+
<if_group>agentsniff</if_group>
113+
<field name="summary.total_agents">\d+</field>
114+
<description>AgentSniff scan found $(summary.total_agents) agent(s).</description>
115+
</rule>
116+
117+
<!-- ── Confirmed agent (AgentPin or MCP verified) ─────────────── -->
118+
<rule id="100250" level="10">
119+
<if_sid>100240</if_sid>
120+
<field name="agents.confidence_level">confirmed</field>
121+
<description>AgentSniff CONFIRMED agent: $(agents.framework) at $(agents.ip_address):$(agents.port).</description>
122+
<options>alert_by_email</options>
123+
</rule>
124+
125+
<!-- ── High confidence agent ──────────────────────────────────── -->
126+
<rule id="100251" level="9">
127+
<if_sid>100240</if_sid>
128+
<field name="agents.confidence_level">high</field>
129+
<description>AgentSniff HIGH confidence agent: $(agents.framework) at $(agents.ip_address):$(agents.port).</description>
130+
</rule>
131+
132+
<!-- ── Medium confidence agent ────────────────────────────────── -->
133+
<rule id="100252" level="7">
134+
<if_sid>100240</if_sid>
135+
<field name="agents.confidence_level">medium</field>
136+
<description>AgentSniff MEDIUM confidence agent: $(agents.framework) at $(agents.ip_address):$(agents.port).</description>
137+
</rule>
138+
139+
<!-- ── Low confidence agent ───────────────────────────────────── -->
140+
<rule id="100253" level="5">
141+
<if_sid>100240</if_sid>
142+
<field name="agents.confidence_level">low</field>
143+
<description>AgentSniff LOW confidence agent: $(agents.framework) at $(agents.ip_address):$(agents.port).</description>
144+
</rule>
145+
146+
<!-- ── Specific detector signals ──────────────────────────────── -->
147+
148+
<!-- MCP server confirmed -->
149+
<rule id="100260" level="10">
150+
<if_sid>100240</if_sid>
151+
<field name="agents.signals.detector">mcp_detector</field>
152+
<description>AgentSniff found MCP server at $(agents.ip_address):$(agents.port).</description>
153+
</rule>
154+
155+
<!-- AgentPin identity verified -->
156+
<rule id="100261" level="10">
157+
<if_sid>100240</if_sid>
158+
<field name="agents.signals.detector">agentpin_prober</field>
159+
<description>AgentSniff verified AgentPin identity at $(agents.ip_address).</description>
160+
</rule>
161+
162+
<!-- DNS query to LLM API -->
163+
<rule id="100262" level="7">
164+
<if_sid>100240</if_sid>
165+
<field name="agents.signals.detector">dns_monitor</field>
166+
<description>AgentSniff detected LLM API DNS queries from $(agents.ip_address).</description>
167+
</rule>
168+
169+
<!-- Known agent framework endpoint -->
170+
<rule id="100263" level="8">
171+
<if_sid>100240</if_sid>
172+
<field name="agents.signals.detector">endpoint_prober</field>
173+
<description>AgentSniff found agent framework endpoint at $(agents.ip_address):$(agents.port).</description>
174+
</rule>
175+
176+
<!-- Traffic pattern matches agent behavior -->
177+
<rule id="100264" level="7">
178+
<if_sid>100240</if_sid>
179+
<field name="agents.signals.detector">traffic_analyzer</field>
180+
<description>AgentSniff detected agent traffic patterns from $(agents.ip_address).</description>
181+
</rule>
182+
183+
<!-- nmap enrichment excluded a false positive -->
184+
<rule id="100265" level="3">
185+
<if_sid>100240</if_sid>
186+
<field name="agents.status">info</field>
187+
<description>AgentSniff nmap enrichment excluded non-agent service at $(agents.ip_address):$(agents.port).</description>
188+
</rule>
189+
190+
<!-- ── Multiple agents on same scan (correlation) ─────────────── -->
191+
<rule id="100270" level="12" frequency="5" timeframe="60">
192+
<if_matched_sid>100250</if_matched_sid>
193+
<description>AgentSniff detected 5+ confirmed agents in 60 seconds — possible agent cluster.</description>
194+
<options>alert_by_email</options>
195+
</rule>
196+
197+
<rule id="100271" level="11" frequency="10" timeframe="300">
198+
<if_matched_sid>100251</if_matched_sid>
199+
<description>AgentSniff detected 10+ high-confidence agents in 5 minutes — large agent presence.</description>
200+
<options>alert_by_email</options>
201+
</rule>
202+
203+
</group>

0 commit comments

Comments
 (0)