Tools · Nmap and network discovery · 23 / 25
Detecting Nmap scans with Suricata: rules, thresholds, and testing
Write a threshold-based Suricata rule for SYN sweeps, recognize built-in scan signatures, and test detection against your own lab scan.
Signatures versus thresholds
Suricata, like Snort, inspects packets against rules. Some community rules match Nmap-specific artifacts, such as the user agent of NSE HTTP scripts, “Mozilla/5.0 (compatible; Nmap Scripting Engine)”. Others use thresholds: alert when one source sends more than N SYNs to distinct ports in T seconds.
Signature rules are precise but easy to sidestep with a changed user agent. Threshold rules catch any scanner, including Nmap’s raw SYN scan, but need tuning so legitimate busy clients do not trigger them.
A simple threshold rule
The rule below matches TCP packets with only SYN set from outside the home network and uses detection_filter to alert once a source exceeds 100 matches in 60 seconds. Put it in a local rules file, include that file in suricata.yaml, and reload.
Set HOME_NET correctly first. Most noisy or silent Suricata deployments trace back to address variables that do not match the real network.
alert tcp $EXTERNAL_NET any -> $HOME_NET any (msg:"LOCAL possible TCP SYN scan"; flags:S,12; \ detection_filter:track by_src, count 100, seconds 60; classtype:attempted-recon; sid:9000001; rev:1;)
Lab: prove the rule fires
On a lab sensor running Suricata against the host-only interface, load the rule and run a SYN scan from the scanner VM. Check eve.json for the alert with jq. Then run a single connection with curl and confirm it does not fire. Finally run an NSE HTTP script and look for the community user-agent signature if you have the ET Open ruleset loaded.
Record the scan command, the alert, and the timestamp. A detection that has never been tested is a hope, and this lab turns it into evidence.
sudo suricata -T -c /etc/suricata/suricata.yaml sudo systemctl restart suricata sudo nmap -sS --top-ports 500 192.168.56.101 # from scanner jq 'select(.event_type=="alert") | [.timestamp, .src_ip, .alert.signature]' /var/log/suricata/eve.json | tail
Tuning for real networks
Exclude your sanctioned vulnerability scanners by address with a pass rule or a suppress entry in threshold.config, and document the exclusion. Otherwise the scanner becomes noise and real scans hide behind it.
For internal networks, lower the count. A workstation sending SYNs to 100 ports in a minute is far more unusual than an internet host doing the same to your edge.
Diagram
From rule to tested detection
- Variables
Set HOME_NET and EXTERNAL_NET to the real ranges.
- Rule
Add a SYN threshold rule with detection_filter.
- Test
Scan from the lab and confirm one alert in eve.json.
jq 'select(.event_type=="alert")' eve.json
- Tune
Suppress sanctioned scanners and lower thresholds internally.
Check yourself
1. Which Suricata keyword makes a rule alert only after a count is exceeded?
2. Why are user-agent signatures for NSE weaker than threshold rules?
3. Your vulnerability scanner triggers the rule weekly. What is the right fix?