HackBlue

Tools · Network IDS and detection as code · 02 / 04

Suricata local rules: write one for your network and tune a noisy one

Write a local Suricata rule that flags DNS lookups of a retired internal domain, test it against a lab capture, and suppress a noisy ET rule for one known host with a threshold entry.

12 min read · practice only with authorization

Rules that only your network needs

Public rulesets know about public threats. They do not know that billing-v1.hackblue-lab.org was retired and that any lookup of it means a client still has old configuration. A local rule turns that knowledge into an alert. Local rules use SIDs from 1000000 to 1999999 and live in their own file.

The rule

dns.query is a sticky buffer: content that follows it matches only the queried name. nocase handles mixed-case lookups. rev increments every time the rule changes.

Snippet
# /etc/suricata/rules/local.rules
alert dns $HOME_NET any -> any any (msg:"HB local - lookup of retired billing-v1 host"; \
  dns.query; content:"billing-v1.hackblue-lab.org"; nocase; endswith; \
  classtype:policy-violation; sid:1000101; rev:2;)

# suricata.yaml
rule-files:
  - suricata.rules
  - /etc/suricata/rules/local.rules

Proving it fires, and only when it should

Record a lab client resolving the name, replay it, and count alerts for the SID. Then replay a capture with ordinary traffic and confirm zero. Both pcaps go into the repository next to the rule as regression tests.

Snippet
dig @10.8.0.53 billing-v1.hackblue-lab.org   # on the lab client while capturing
suricata -r /lab/pcaps/retired-lookup.pcap -l /tmp/t1 -k none
jq 'select(.alert.signature_id==1000101)' /tmp/t1/eve.json | jq -s length
1
suricata -r /lab/pcaps/normal-day.pcap -l /tmp/t2 -k none
jq 'select(.alert.signature_id==1000101)' /tmp/t2/eve.json | jq -s length
0

Worked tuning: a scanner that is supposed to scan

The vulnerability scanner at 10.8.0.5 triggers an ET SCAN rule thousands of times a week. Disabling the rule would hide a real scan from anywhere else. A suppress entry in threshold.config silences it for that source only, and a comment names the owner of the scanner.

Snippet
# /etc/suricata/threshold.config
# Nessus scanner, owner: vuln-mgmt team, reviewed 2026-11-01
suppress gen_id 1, sig_id 2010937, track by_src, ip 10.8.0.5

sudo suricatasc -c reload-rules

Diagram

Disabling a rule versus suppressing one source

Suppression keeps coverage for everyone else.

Disable the SID

Effect. No alerts from anyone

Risk. A real scan from a new source goes unseen

Use when. The rule is wrong for your network

Suppress by source

Effect. No alerts from 10.8.0.5 only

Risk. Small, if the scanner is controlled

Use when. One known system is the noise

Check yourself

1. Which SID range is meant for local rules?

2. What does dns.query do in the rule?

3. A known scanner triggers an ET rule constantly. What keeps coverage for other sources?