Snort and Suricata: Network IDS in Your Home Lab and Your First Rules
Part 7 of Tools & Targets: place a network sensor, install Snort or Suricata, keep rules updated with suricata-update, write and test local rules for ICMP, SYN scans, HTTP paths and cleartext FTP, and read alerts with jq.
Part of our free Tools & Targets series. Hands-on guides to the vulnerable practice apps and the security tools you will use on the job. Pair it with our Build Your Own Cyber Lab series for the lab to run them in.
- Vulnerable practice apps: DVWA, bWAPP, Juice Shop, WebGoat, AI Goat and more
- DVWA walkthrough: every module solved at Low, Medium and High
- OWASP Juice Shop: beginner and intermediate challenges solved
- WebGoat and bWAPP: key lessons solved
- Threat modeling your lab with OWASP Threat Dragon
- Nessus Essentials: vulnerability scanning your lab
- Snort and Suricata: network IDS and your first rules (you are here)
- Splunk in your lab: install, forward Windows logs, search with SPL
- Wazuh deep dive: file integrity, vulnerability detection and custom rules
What a network IDS does
A network intrusion detection system watches packets on a link and compares them with signatures (rules). When a packet or a stream matches, it raises an alert. Snort (now maintained by Cisco Talos) and Suricata (from the Open Information Security Foundation) are the two open-source engines you will meet in SOC jobs, inside Security Onion, and on CySA+ and Security+ exams. Their rule languages are close relatives, so learning one teaches you most of the other.
Step 1: Place the sensor where it can see traffic
An IDS can only alert on packets it receives. In a home lab:
- Create an Ubuntu Server VM (2 CPUs, 4 GB RAM is enough for a small lab) with two adapters: one management adapter and one sniffing adapter attached to the network you want to watch - the isolated ATTACK network with Kali and your vulnerable targets.
- In the hypervisor settings for the sniffing adapter, set promiscuous mode to allow all (VirtualBox: Allow All; VMware and Proxmox have equivalent switch or bridge settings). Without this, the sensor only sees traffic addressed to itself.
- Bring the interface up without an IP address and confirm you can see other hosts' packets:
sudo ip link set eth1 up # use your sniffing interface name
sudo tcpdump -i eth1 -c 20Ping Metasploitable from Kali while tcpdump runs. If you see those ICMP packets, the sensor is placed correctly.
Step 2: Choose and install an engine
Snort: check which major version you are getting
Snort 3 is the current generation (Lua configuration in snort.lua, a rewritten rule parser). Snort 2.9 is the older generation; Talos now focuses on Snort 3, but 2.9 is still common in older guides and some distribution packages. Run apt show snort before installing: if the package version starts with 2.9, you are getting Snort 2. For Snort 3, follow the installation section of the Snort 3 documentation, which builds it from source with its dependencies. Rule syntax below notes where the two differ.
Suricata: the simpler route on Ubuntu
The OISF maintains a stable PPA, documented on Suricata's Ubuntu installation page:
sudo apt-get install software-properties-common
sudo add-apt-repository ppa:oisf/suricata-stable
sudo apt-get update
sudo apt-get install suricataThen edit /etc/suricata/suricata.yaml:
- Set
HOME_NETto your lab range (for example"[10.10.10.0/24]"). - Under
af-packet, setinterfaceto your sniffing adapter.
Fetch the free rule sets (Emerging Threats Open by default) with suricata-update, which writes a merged file to /var/lib/suricata/rules/suricata.rules:
sudo suricata-update
sudo systemctl restart suricataRun suricata-update regularly (a cron job or systemd timer) so signatures stay current.
Step 3: Anatomy of a rule
Every rule has a header and options:
alert tcp $EXTERNAL_NET any -> $HOME_NET 21 (msg:"LOCAL FTP login attempt"; flow:to_server,established; content:"USER "; sid:1000004; rev:1;)| Part | Example | Meaning |
|---|---|---|
| Action | alert | What to do on a match (alert, drop in IPS mode, pass) |
| Protocol | tcp | tcp, udp, icmp, ip, or an application protocol such as http |
| Source and port | $EXTERNAL_NET any | Where the packet comes from |
| Direction | -> | One way (<> matches both ways) |
| Destination and port | $HOME_NET 21 | Where it is going |
msg | "LOCAL FTP login attempt" | Text shown in the alert |
flow | to_server,established | Only packets from client to server on an established session |
content | "USER " | Bytes that must appear in the payload |
sid / rev | 1000004 / 1 | Unique rule ID and its revision |
Local rules use sid 1000000 and above. Lower ranges belong to the official and community rule sets; reusing them causes collisions.
Step 4: Write your first four rules
Create a local rules file. For Suricata use /etc/suricata/rules/local.rules and add its full path to the rule-files list in suricata.yaml. For Snort 3, keep it beside your configuration and load it with -R or an ips include in snort.lua.
Rules that work in both engines
alert icmp any any -> $HOME_NET any (msg:"LOCAL ICMP test"; itype:8; sid:1000001; rev:1;)
alert tcp any any -> $HOME_NET any (msg:"LOCAL possible SYN scan"; flags:S; flow:stateless; detection_filter:track by_src, count 20, seconds 10; sid:1000002; rev:1;)- Rule 1000001 fires on every ICMP echo request (type 8) - a sanity check that the pipeline works.
- Rule 1000002 matches bare SYN packets and uses
detection_filterso it only alerts once a single source has sent more than 20 in 10 seconds. A normal client opening a few connections stays quiet; a port scan does not.
Snort 2 also had a threshold keyword; Snort 3 moved rate controls into event_filter configuration, and Suricata treats threshold and detection_filter differently (see its thresholding page). detection_filter is the portable choice here.
Rules where the syntax differs
Suricata uses sticky buffers with dotted names and separate modifier keywords:
alert http any any -> $HOME_NET any (msg:"LOCAL request for /admin path"; flow:established,to_server; http.uri; content:"/admin"; nocase; sid:1000003; rev:1;)
alert tcp any any -> $HOME_NET 21 (msg:"LOCAL cleartext FTP login"; flow:established,to_server; content:"USER "; depth:5; sid:1000004; rev:1;)Snort 3 uses its own buffer names and puts content modifiers after a comma:
alert http (msg:"LOCAL request for /admin path"; flow:established,to_server; http_uri; content:"/admin",nocase; sid:1000003; rev:1;)
alert tcp any any -> $HOME_NET 21 (msg:"LOCAL cleartext FTP login"; flow:established,to_server; content:"USER ",depth 5; sid:1000004; rev:1;)(Snort 3 service rules such as alert http may omit the addresses and ports, because the HTTP inspector identifies the service.) If a rule fails to load, the engine prints the line and the option it could not parse - check it against the rule documentation for your engine and version.
Test the configuration before restarting
# Suricata: test the configuration and rules, then restart
sudo suricata -T -c /etc/suricata/suricata.yaml -v
sudo systemctl restart suricata
# Snort 3: test mode, then run on the sniffing interface with fast alerts to the console
snort -c /usr/local/etc/snort/snort.lua -R local.rules -T
sudo snort -c /usr/local/etc/snort/snort.lua -R local.rules -i eth1 -A alert_fast -k noneThe snort.lua path depends on how you installed Snort 3, and -k none stops Snort ignoring packets with offloaded checksums, which is common on virtual adapters.
Step 5: Fire the rules from Kali
From Kali on the ATTACK network, aim at Metasploitable 2 only:
ping -c 3 <metasploitable-ip> # triggers 1000001
sudo nmap -sS -p 1-1000 <metasploitable-ip> # triggers 1000002
curl http://<metasploitable-ip>/admin # triggers 1000003 (a 404 still matches)
ftp <metasploitable-ip> # log in to trigger 1000004The FTP rule is a lesson in itself: Metasploitable's FTP server sends usernames and passwords in cleartext, so an IDS (or an attacker with a sniffer) sees them.
Step 6: Read the alerts
Snort 3 with -A alert_fast prints one line per alert to the console; other loggers write files - see the logging section of the Snort 3 docs.
Suricata writes one-line alerts to /var/log/suricata/fast.log and rich JSON events to /var/log/suricata/eve.json. Use jq to pull just the alerts:
tail -f /var/log/suricata/fast.log
jq -c 'select(.event_type=="alert") | {ts: .timestamp, src: .src_ip, dst: .dest_ip, sid: .alert.signature_id, sig: .alert.signature}' /var/log/suricata/eve.jsonEVE also logs flows, HTTP, DNS and TLS metadata even when nothing alerts, which is exactly what you want when hunting. Count alerts by signature to see what is noisy:
jq -r 'select(.event_type=="alert") | .alert.signature' /var/log/suricata/eve.json | sort | uniq -c | sort -rnStep 7: Tune false positives
A rule that fires constantly gets ignored, and then the real alert is missed. Tune deliberately:
- Narrow the rule - add
flow, a port, a tightercontentmatch or a buffer such ashttp.uriinstead of matching the whole payload. - Rate-limit noisy but useful rules with
detection_filteror the engine's threshold/event filter configuration. - Suppress a known-good source (your vulnerability scanner from part 6, for example) for one sid, rather than disabling the rule for everyone.
- Disable rules irrelevant to your lab through
suricata-update's disable configuration, so the change survives the next update. - Bump
revevery time you edit a rule, and keep local rules in version control.
Keep it legal and contained
Only scan, probe or attack machines you own or have written permission to test. Keep vulnerable targets on an isolated lab network with no route to your home LAN or the internet, and never point a scanner or exploit at an address you do not control - even "just to see" is unauthorised access in most jurisdictions.
Next step
Part 8 sends these alerts and your Windows logs somewhere you can search them: Splunk in your lab. Meanwhile, practise reading the traffic behind the alerts in our packet lab and PCAP analysis lab, and write and tune signatures against realistic data in the detection engineering lab.
Sources & References
Daniel Agrici
CEH, Security+, PenTest+
Daniel is the founder of CyberCertPrep. With a background in penetration testing and security consulting, he has passed 8 cybersecurity certifications and writes about exam strategies and career development.
Ready to start practicing?
86 certifications. 157,000+ questions. 20 free per cert.