In this module
NF1.7 Sensor Validation
You've installed Zeek and Suricata separately and verified each produces output. This sub validates the complete sensor — both tools processing the same traffic, Community ID correlation working, all expected log files present, and no evidence gaps.
A sensor that appears to work can have silent failures — Zeek dropping a protocol analyzer, Suricata loading with zero rules, Community ID not matching between tools, or packet loss going undetected. Validation catches these before they become evidence gaps in a real investigation.
This sub runs a structured validation test: process a known PCAP through both tools, verify all expected outputs, confirm cross-tool correlation, and establish a baseline for sensor health checks going forward.
Deliverable: A validated sensor with documented proof that Zeek produces complete logs, Suricata fires expected alerts, Community ID correlation works, and no evidence gaps exist for the test PCAP.
Figure NF1.7 — The four validation checks. All four must pass before the sensor is considered operational. Failing any check means an evidence gap that will affect investigations in subsequent modules.
The Validation Test
Run this complete validation script against a known-malicious PCAP. Download a PCAP from malware-traffic-analysis.net that contains identifiable C2 traffic (the site labels them by malware family — pick one that includes HTTP or TLS traffic for the fullest log coverage).
#!/bin/bash
# Sensor Validation Script — run from /opt/sensor/
PCAP="/opt/sensor/pcap/validation-test.pcap"
ZEEK_OUT="/opt/sensor/zeek-logs/validation/"
SURI_OUT="/opt/sensor/suricata-logs/"
echo "=== SENSOR VALIDATION ==="
echo "PCAP: $PCAP"
echo "Date: $(date -u)"
echo ""
# Step 1: Run Zeek
mkdir -p "$ZEEK_OUT" && cd "$ZEEK_OUT"
rm -f *.log 2>/dev/null
zeek -r "$PCAP"
echo "--- ZEEK LOG FILES ---"
ls -lh *.log 2>/dev/null
CONN_COUNT=$(grep -v '^#' conn.log 2>/dev/null | wc -l)
DNS_COUNT=$(grep -v '^#' dns.log 2>/dev/null | wc -l)
echo "Connections: $CONN_COUNT"
echo "DNS queries: $DNS_COUNT"
echo "Community ID present: $(head -20 conn.log | grep -c community_id)"
echo ""
# Step 2: Run Suricata
rm -f "$SURI_OUT/eve.json" 2>/dev/null
sudo suricata -r "$PCAP" -l "$SURI_OUT" \
--set classification-file=/etc/suricata/classification.config \
--set reference-config-file=/etc/suricata/reference.config 2>/dev/null
ALERT_COUNT=$(cat "$SURI_OUT/eve.json" 2>/dev/null | jq 'select(.event_type=="alert")' | grep -c '"alert"')
echo "--- SURICATA ---"
echo "Alerts: $ALERT_COUNT"
echo ""
# Step 3: Community ID correlation
echo "--- COMMUNITY ID CORRELATION ---"
CID=$(cat "$SURI_OUT/eve.json" | jq -r 'select(.event_type=="alert") | .community_id' 2>/dev/null | head -1)
if [ -n "$CID" ] && [ "$CID" != "null" ]; then
MATCH=$(grep -c "$CID" "$ZEEK_OUT/conn.log")
echo "Suricata CID: $CID"
echo "Zeek conn.log match: $MATCH entries"
else
echo "No alerts with Community ID found"
fi
echo ""
# Step 4: Packet loss
echo "--- PACKET LOSS ---"
if [ -f "$ZEEK_OUT/capture_loss.log" ]; then
LOSS=$(grep -v '^#' "$ZEEK_OUT/capture_loss.log" | wc -l)
echo "capture_loss entries: $LOSS"
else
echo "No capture_loss.log (good — no loss detected)"
fi
echo ""
echo "=== VALIDATION COMPLETE ==="Save this script as /opt/sensor/validate-sensor.sh and run it:
chmod +x /opt/sensor/validate-sensor.sh
/opt/sensor/validate-sensor.shInterpreting Results
Zeek logs complete: You should see conn.log with hundreds or thousands of entries, dns.log with query records, and at least one protocol-specific log (ssl.log for HTTPS traffic, http.log for HTTP). If conn.log is empty, Zeek failed to process the PCAP — check for error messages.
Suricata alerts fire: If the PCAP contains known malicious traffic and the ET Open rules are loaded, you should see alerts. If the alert count is zero, either the PCAP doesn't contain traffic matching ET rules, or the rules aren't loaded. Verify with wc -l /var/lib/suricata/rules/suricata.rules.
Community ID match: The same Community ID should appear in both a Suricata alert and a Zeek conn.log entry. If the IDs don't match, verify both tools have Community ID enabled and are using the same hash algorithm version.
Zero packet loss: For PCAP replay, there should be no packet loss (the file is read from disk, not captured live). If capture_loss.log has entries, it may indicate Zeek couldn't process packets fast enough — unlikely for offline replay, but possible with extremely large PCAPs on low-spec VMs.
After all four checks pass, take a VM snapshot: "Sensor validated — ready for NF2." This is your known-good state. If anything breaks in later modules, restore to this snapshot.
Your validation shows Zeek producing logs and Suricata firing alerts, but the Community ID from Suricata doesn't match any entry in Zeek's conn.log.
This can happen when the Community ID implementation differs between versions — Zeek and Suricata must both use Community ID v1 for the hashes to match. Alternatively, the Suricata alert may reference a flow that Zeek classified differently (e.g., Suricata sees it as a TCP flow, Zeek sees it as part of a different connection due to TCP reassembly differences).
The fix: verify Zeek has @load policy/protocols/conn/community-id-logging in local.zeek. Verify Suricata has community-id: enabled: true in suricata.yaml. Re-run both tools against the same PCAP. If the hashes still don't match, extract the 5-tuple (source IP, source port, destination IP, destination port, protocol) from the Suricata alert and search for it manually in conn.log — this confirms the same flow exists in both tools even if the hash calculation differs.
A sensor that ran perfectly in April can fail silently in June. Suricata rules expire as threat intelligence evolves. Zeek protocol parsers can break after OS updates. Disk space fills. NTP synchronisation drifts. The sensor process crashes at 03:00 and nobody notices until the next investigation discovers a 12-hour evidence gap.
Sensor maintenance is ongoing — NF1.9 covers the maintenance checklist. Validation isn't a one-time event. Run the validation script monthly, after any system update, and after any Zeek or Suricata version upgrade.
Try it: Run the full validation and document the results
Setup. Your sensor VM with Zeek and Suricata installed, a PCAP containing identifiable malicious traffic.
Task. Run the validation script from this sub. Save the output to a file: /opt/sensor/validate-sensor.sh | tee /opt/sensor/validation-$(date +%Y%m%d).txt. Review the output and verify all four checks pass.
Expected result. A validation report saved to disk showing: Zeek log file list with row counts, Suricata alert count, Community ID correlation match, and packet loss status. All checks green.
Debugging branch. If any check fails, re-read the specific NF1 sub that covers that component: Zeek (NF1.3), Suricata (NF1.5), Community ID (NF1.3 and NF1.5 configuration sections).
You've built the sensor and mapped the evidence landscape.
NF0 established why network evidence matters when every other source is compromised. NF1 built your Zeek + Suricata sensor with the 10 investigation query patterns. From here, every module teaches protocol-specific investigation against real attack scenarios.
- DNS deep dive (NF3) — tunnelling detection, DGA analysis, passive DNS infrastructure mapping, and the INC-NE-2026-0227 AiTM phishing DNS trail
- Protocol analysis (NF4–NF7) — HTTP/HTTPS, SMB lateral movement, SSH tunnelling, and email protocol investigation with Zeek metadata and PCAP
- Detection and hunting (NF8–NF11) — Suricata rule writing, C2 beacon detection with JA3, NetFlow analytics, and proactive network threat hunting
- NSM architecture (NF13) — production sensor deployment at 1–10 Gbps with Arkime, Security Onion, and enterprise storage planning
- INC-NE-2026-0830 capstone (NF14) — multi-stage investigation using only network evidence: phishing → domain-fronted C2 → lateral movement → DNS tunnel exfiltration
Cancel anytime