IDS/IPS

Intrusion detection and prevention — Snort and Suricata rule management and tuning.

IDS/IPS — Suricata and Snort

IDS vs IPS

IDS (Intrusion Detection System):
  - Passive — monitors a copy of traffic (span port or tap)
  - Alerts only, does not block
  - No risk of breaking production traffic
  - Mode: af-packet with copy, or pcap

IPS (Intrusion Prevention System):
  - Inline — sits in the traffic path
  - Can drop, reject, or modify packets
  - Risk: false positive = legitimate traffic blocked
  - Mode: af-packet inline, netfilter (NFQUEUE)

Suricata Basics

Start Suricata in IDS mode — passive monitoring
sudo suricata -c /etc/suricata/suricata.yaml -i eth0
Start Suricata in IPS mode — inline with NFQUEUE
# First configure iptables to send traffic to NFQUEUE
sudo iptables -I FORWARD -j NFQUEUE --queue-num 0
sudo suricata -c /etc/suricata/suricata.yaml -q 0
Run Suricata against a pcap file — offline analysis
sudo suricata -c /etc/suricata/suricata.yaml -r capture.pcap -l /var/log/suricata/
Check Suricata service status
sudo systemctl status suricata
sudo suricatasc -c "uptime"
Update Suricata rules — pull latest from enabled sources
sudo suricata-update
sudo suricata-update list-sources
sudo suricata-update enable-source et/open
sudo systemctl restart suricata

Suricata Rule Syntax

Rule structure breakdown
action  protocol  src_ip  src_port  ->  dst_ip  dst_port  (rule options)

action:    alert | pass | drop | reject
protocol:  tcp | udp | icmp | http | dns | tls | ssh
direction: -> (to dst) | <> (bidirectional)
Alert on SSH brute force — 5 failed logins in 60 seconds
alert ssh any any -> $HOME_NET 22 (msg:"SSH Brute Force Attempt"; \
  flow:to_server,established; \
  threshold:type both, track by_src, count 5, seconds 60; \
  classtype:attempted-admin; sid:1000001; rev:1;)
Alert on outbound DNS to non-standard port — possible tunneling
alert udp $HOME_NET any -> !$DNS_SERVERS any (msg:"DNS Query to Non-Standard Server"; \
  dns.query; content:"."; \
  classtype:policy-violation; sid:1000002; rev:1;)
Alert on HTTP containing suspicious user-agent
alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"Suspicious User-Agent - Possible C2"; \
  http.user_agent; content:"Mozilla/4.0"; \
  content:!"MSIE"; \
  classtype:trojan-activity; sid:1000003; rev:1;)
Drop ICMP tunneling — large ICMP packets (IPS mode)
drop icmp any any -> any any (msg:"ICMP Tunnel - Oversized Payload"; \
  dsize:>800; \
  classtype:policy-violation; sid:1000004; rev:1;)
Detect Mimikatz string in network traffic
alert tcp any any -> $HOME_NET any (msg:"Possible Mimikatz Usage"; \
  flow:established; \
  content:"mimikatz"; nocase; \
  content:"sekurlsa"; nocase; distance:0; \
  classtype:credential-access; sid:1000005; rev:1;)

Suricata Log Files

fast.log — one-line alert summaries (quick triage)
# Watch alerts in real time
tail -f /var/log/suricata/fast.log

# Count alerts by signature
awk -F'\\[\\*\\*\\]' '{print $2}' /var/log/suricata/fast.log \
  | sort | uniq -c | sort -rn | head -20
eve.json — full structured JSON log (rich analysis)
# Parse alerts from eve.json
jq 'select(.event_type=="alert") | {timestamp, src_ip, dest_ip, alert: .alert.signature}' \
  /var/log/suricata/eve.json | head -50

# Count alerts by severity
jq -r 'select(.event_type=="alert") | .alert.severity | tostring' \
  /var/log/suricata/eve.json | sort | uniq -c | sort -rn

# Extract DNS queries
jq 'select(.event_type=="dns") | {timestamp, src_ip, query: .dns.rrname, type: .dns.rrtype}' \
  /var/log/suricata/eve.json | head -20

# TLS fingerprints — JA3 hashes
jq 'select(.event_type=="tls") | {timestamp, src_ip, dest_ip, ja3: .tls.ja3.hash, sni: .tls.sni}' \
  /var/log/suricata/eve.json | head -20
stats.log — performance counters
# Check for packet drops — drops mean Suricata can't keep up
awk '/capture.kernel_drops/ {print}' /var/log/suricata/stats.log | tail -5

Snort vs Suricata Comparison

Feature Suricata Snort 3

Threading

Multi-threaded (worker, autofp)

Multi-threaded (Snort 3)

Protocol parsing

Built-in (HTTP, TLS, DNS, SSH, SMB)

Inspectors (HTTP, SSL, DNS)

Output format

EVE JSON (structured)

Unified2, JSON (Snort 3)

Rule format

Snort-compatible + extensions

Snort rule language

Lua scripting

Yes — custom detection scripts

Yes — via inspectors

JA3/JA3S

Native support

Plugin required

File extraction

Built-in

Built-in (Snort 3)

IPS mode

af-packet inline, NFQUEUE

af-packet inline, DAQ

Performance Tuning

Suricata threading — configure for available CPU cores
# /etc/suricata/suricata.yaml
threading:
  set-cpu-affinity: yes
  detect-thread-ratio: 1.5  # 1.5x detection threads per capture thread

af-packet:
  - interface: eth0
    threads: 4               # Match physical cores
    cluster-type: cluster_flow
    defrag: yes
Tune rule loading — disable noisy rules that generate false positives
# Disable a specific rule by SID
echo "1:2100498" >> /etc/suricata/disable.conf
suricata-update
Monitor Suricata performance — check for processing bottlenecks
sudo suricatasc -c "dump-counters" | jq '.message | {
  packets: .capture.kernel_packets,
  drops: .capture.kernel_drops,
  alerts: .detect.alert,
  flows: .flow.tcp
}'

Wazuh Integration

Wazuh reads Suricata’s eve.json natively. Configure the log collector on the Wazuh agent.

Configure Wazuh agent to read Suricata alerts
<!-- /var/ossec/etc/ossec.conf on the Suricata host -->
<localfile>
  <log_format>json</log_format>
  <location>/var/log/suricata/eve.json</location>
</localfile>

Wazuh has built-in decoders and rules for Suricata (rule group suricata). Alerts appear in the Wazuh dashboard with full Suricata metadata — source/dest IP, protocol, signature, severity.

Create custom Wazuh rule for high-severity Suricata alerts
<!-- /var/ossec/etc/rules/local_rules.xml -->
<group name="suricata,">
  <rule id="100010" level="12">
    <if_sid>86601</if_sid>
    <field name="alert.severity">1</field>
    <description>Suricata: Critical severity alert — $(alert.signature)</description>
  </rule>
</group>

Bypass / Pass Rules

Allow traffic from a trusted scanner — prevent alert floods during vuln scans
pass tcp 10.50.1.200 any -> $HOME_NET any (msg:"Allow Nessus Scanner"; sid:1000099; rev:1;)

Pass rules must appear before alert rules in the rule file, or use a lower sid. Suricata evaluates pass rules first when rule-action-order is set to pass, drop, reject, alert.