Project

General

Profile

Bug #8940 » Suricata漏检复现说明.md

JIAXIN TAN, 08/24/2026 07:30 AM

 

Bug Report: HTTP traffic detected by Zeek but missed by Suricata (af-packet cluster_cpu breaks TCP stream reassembly)


Summary

A scanner on host 10.10.168.224 sends normal, well-formed HTTP requests (complete 3-way handshake, valid sequence numbers, server returns 404) to 10.10.168.197:80.

  • Zeek (libpcap, same interface eno1np0) logs all HTTP transactions correctly.
  • Suricata (live) produces zero events for these flows — not even a flow event in eve.json.
  • Suricata (offline -r replay of the same pcap) matches the rules 100%.

The traffic does reach the monitored interface; the problem is in Suricata's live capture → stream pipeline.


Environment

Deployment (Docker Compose, host network)

Both engines listen on the same physical interface eno1np0 (SPAN mirror port):

Suricata Zeek
Image jasonish/suricata:latest (8.0.6) zeek/zeek:latest (8.2.0)
Network host host
CapAdd CAP_NET_ADMIN CAP_NET_RAW CAP_SYS_NICE CAP_NET_ADMIN CAP_NET_RAW
Interface eno1np0 eno1np0
Cmd -i eno1np0 zeek -i eno1np0 ...
Capture af-packet libpcap

af-packet config (suricata.yaml)

af-packet:
  - interface: default        # wildcard; real iface passed via -i eno1np0
    threads: 16
    cluster-id: 99
    cluster-type: cluster_cpu # <-- load-balances by CPU, NOT by flow
    ring-size: 65536

Host NIC

eno1np0  : SPAN mirror port (PROMISC, no IP), monitored by both engines
eno3np2  : management NIC, host IP 10.10.64.113/24

Evidence

1. Two capture points see identical packets (traffic DOES reach the mirror port)

While the attack was running, we captured eno1np0 at two points simultaneously (host 10.10.168.197 and tcp port 80):

Capture point File MD5
Inside Suricata container (tcpdump -i eno1np0) suri_capture.pcap 819fdd3f37445b93bd0e054593878078
Host (tcpdump -i eno1np0) host_capture.pcap 819fdd3f37445b93bd0e054593878078

Both pcaps are byte-identical. The captured flows are complete and well-formed:

10.10.168.224.57707 > 10.10.168.197.80  GET /.git/index HTTP/1.1
10.10.168.197.80  > 10.10.168.224.57707 HTTP/1.1 404 Not Found (720 bytes)
10.10.168.224.57709 > 10.10.168.197.80  GET /WEB-INF/web.xml HTTP/1.1
10.10.168.197.80  > 10.10.168.224.57709 HTTP/1.1 404 Not Found (720 bytes)
10.10.168.224.57710 > 10.10.168.197.80  GET /druid/index.html HTTP/1.1
10.10.168.197.80  > 10.10.168.224.57710 HTTP/1.1 404 Not Found (720 bytes)
... (6 attack URIs total, all tagged vlan 168)

2. Live Suricata produces zero events for these flows

# inside the suricata container, search eve.json for the attack source ports
grep -E "57707|57708|57709|57710|57711|57712" /var/log/suricata/eve.json
# → only unrelated DNS (UDP 53) records from OTHER hosts; no http/flow for this TCP stream

3. Offline replay of the same pcap matches rules 100%

suricata -r suri_capture.pcap -c test.yaml -k none -l out
08/24/2026-07:00:15.596530  [1:90015:1]  file read:/.git/ Git 仓库源码泄露    10.10.168.224:57707 -> 10.10.168.197:80
08/24/2026-07:00:15.596530  [1:91025:1]  source leak:GIT 源码目录泄露探测     10.10.168.224:57707 -> 10.10.168.197:80
08/24/2026-07:00:16.176527  [1:91272:1]  auth bypass:Druid 未授权访问漏洞     10.10.168.224:57712 -> 10.10.168.197:80
08/24/2026-07:00:16.176527  [1:91273:1]  auth bypass:若依 未授权访问漏洞      10.10.168.224:57712 -> 10.10.168.197:80

Rules that matched offline (for reference)

alert http any any -> any any (msg:"file read:/.git/ Git 仓库源码泄露"; flow:established,to_server; http.uri; content:"/.git/"; fast_pattern; nocase; classtype:web-application-attack; sid:90015; rev:1;)

alert http any any -> any any (msg:"source leak:GIT 源码目录泄露探测"; flow:established,to_server; http.uri; content:"/.git/"; fast_pattern; classtype:attempted-recon; sid:91025; rev:1;)

alert http any any -> any any (msg:"auth bypass:Druid 未授权访问漏洞"; flow:established,to_server; http.uri; content:"/druid/index.html"; nocase; fast_pattern; classtype:web-application-attack; sid:91272; rev:1;)

alert http any any -> any any (msg:"auth bypass:若依管理系统 未授权访问漏洞"; flow:established,to_server; http.uri; content:"/prod-api/druid/index.html"; nocase; fast_pattern; classtype:web-application-attack; sid:91273; rev:1;)

Root cause hypothesis

stats.log shows massive stream-reassembly failures that correlate with the cluster_cpu load-balancing:

tcp.pkt_on_wrong_thread    = 1,478,842,804   (packets delivered to the wrong thread)
tcp.reassembly_gap         =   369,926,805   (reassembly gaps)
tcp.insert_data_normal_fail =   369,702,088  (insert failures)

With cluster-type: cluster_cpu (16 threads), packets of the same TCP flow get hashed to different CPU threads, producing reassembly gaps and incomplete HTTP streams. libhtp then cannot parse the request URI, so http.uri rules never match. Offline -r replay runs single-threaded and is unaffected — hence the 100% match.

This is the accurate meaning of the forum thread title "HTTP traffic detected by Zeek but missed by Suricata on incomplete TCP streams": it is not that the scanner sent incomplete streams, but that Suricata's thread fan-out broke complete streams apart.


Prior incorrect diagnoses (corrected)

Two earlier hypotheses were ruled out during investigation and are documented here for completeness:

  1. "Scanners send half-open / seq-broken HTTP data" — Incorrect. ES logs (source 1.13.9.36) + Zeek offline analysis confirmed real scanner attack requests are complete, well-formed HTTP (GET + 404). Half-open probes (history:ShAR) are data-less port-liveness checks (nmap -sS), not attack requests.

  2. "Same-VLAN L2 traffic bypasses the SPAN mirror port" — Incorrect. The dual-capture experiment above proves the traffic does reach eno1np0 (byte-identical pcaps at two capture points).


Next steps

  1. Test cluster-type: cluster_flow (flow-hash to keep a flow on one thread) and observe whether tcp.reassembly_gap drops and detection recovers — while monitoring capture.kernel_drops (a prior 12.5% drop issue on a single hot flow was the reason we switched to cluster_cpu, so this is a trade-off to validate).

  2. The attached suri_capture.pcap reproduces "Zeek sees it, Suricata misses it" deterministically and is available for your analysis.

(3-3/3)