HackBlue

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.

11 min read · practice only with authorization

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.

Snippet
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.

Snippet
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.

Snippet
jq -c 'select(.flow_id==1823114407) | {event_type, dns: .dns.rrname, host: .http.hostname, url: .http.url, sha256: .fileinfo.sha256}' /lab/sur-out/eve.json

What 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

  1. Rules

    Fetch ET Open and test the config.

    Command
    suricata-update && suricata -T -c /etc/suricata/suricata.yaml
  2. Replay

    Read the capture and write eve.json.

    Command
    suricata -r ws07-morning.pcap -l /lab/sur-out -k none
  3. Alerts

    List signatures with time and hosts.

    Command
    jq 'select(.event_type=="alert")' eve.json
  4. Context

    Join everything on flow_id.

    Command
    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?