Tools · Network IDS and detection as code · 01 / 04
Suricata offline: run a lab capture through the IDS and read eve.json
Replay a saved lab pcap through Suricata with the ET Open ruleset, then use jq on eve.json to read alerts alongside the DNS, TLS, and flow records that explain them.
Why start offline
Suricata can sit on a live interface, but -r reads a pcap and exits. That makes every run repeatable: the same capture and the same rules give the same output, which is how you learn the event format and later test a rule change.
suricata-update downloads the free ET Open rules into /var/lib/suricata/rules/suricata.rules, the default path the shipped configuration loads.
sudo suricata-update suricata -T -c /etc/suricata/suricata.yaml -v mkdir -p /lab/sur-out suricata -c /etc/suricata/suricata.yaml -r /lab/pcaps/ws07-morning.pcap -l /lab/sur-out -k none
One file, many event types
eve.json holds one JSON object per line. event_type separates alert, dns, http, tls, flow, fileinfo, and stats records. flow_id links every record that belongs to the same connection, which is how an alert gets its context.
jq -r 'select(.event_type=="alert") | [.timestamp, .src_ip, .dest_ip, .alert.signature_id, .alert.signature] | @tsv' /lab/sur-out/eve.json 2026-10-28T09:14:02.118+0000 10.8.0.57 10.8.0.200 2027865 ET INFO Observed DNS Query to .top TLD 2026-10-28T09:14:03.402+0000 10.8.0.57 10.8.0.200 2033966 ET INFO HTTP Request to Suspicious *.top Domain jq -r '.event_type' /lab/sur-out/eve.json | sort | uniq -c
Worked case: following one flow
The HTTP alert has flow_id 1823114407. Selecting every record with that id shows the DNS answer that led to it, the HTTP host and URI, a fileinfo record for a 38 KB download with its SHA-256, and the flow summary with bytes each way.
The hash goes to the malware analysis queue; the host WS-07 goes to the endpoint team with the time and URI. The alert alone said “suspicious domain”; the joined records say what happened.
jq -c 'select(.flow_id==1823114407) | {event_type, dns: .dns.rrname, host: .http.hostname, url: .http.url, sha256: .fileinfo.sha256}' /lab/sur-out/eve.jsonWhat Suricata cannot see
Inside TLS it sees the handshake: SNI, certificate fields, and JA3 or JA4 fingerprints if enabled, not the request. A sensor placed behind a proxy sees the proxy’s address, not the client’s. Note the sensor position in every report so readers know which side of those limits the evidence came from.
Diagram
From pcap to an explained alert
- Rules
Fetch ET Open and test the config.
suricata-update && suricata -T -c /etc/suricata/suricata.yaml
- Replay
Read the capture and write eve.json.
suricata -r ws07-morning.pcap -l /lab/sur-out -k none
- Alerts
List signatures with time and hosts.
jq 'select(.event_type=="alert")' eve.json
- Context
Join everything on flow_id.
jq 'select(.flow_id==1823114407)' eve.json
Check yourself
1. Which field links an alert to the DNS, HTTP, and file records of the same connection?
2. Why use -k none when replaying a capture?
3. What can Suricata report about an HTTPS request without decryption?