Privacy Tools Guide

How to Use Zeek for Network Monitoring

Zeek (formerly Bro) does not match patterns against packets — it reconstructs full sessions and writes structured logs for every protocol it understands. You get a detailed record of every DNS query, HTTP request, TLS handshake, and connection on your network, queryable without replaying PCAPs. This guide covers installation, log analysis, and writing custom detection scripts.

How Zeek Differs from Snort/Suricata

Snort and Suricata match signatures against raw packet bytes. Zeek parses protocols into structured events and exposes them to a scripting engine. The result: Zeek logs are SQL-queryable, schema-consistent, and much richer than IDS alerts — but Zeek doesn’t block traffic by default.

Zeek is commonly paired with a SIEM (Splunk, Elastic, Wazuh) as the data source for threat hunting, rather than as a real-time blocker.


1. Install Zeek

# Ubuntu 24.04 — use the official repository
echo 'deb http://download.opensuse.org/repositories/security:/zeek/xUbuntu_24.04/ /' \
  | sudo tee /etc/apt/sources.list.d/security:zeek.list

curl -fsSL https://download.opensuse.org/repositories/security:zeek/xUbuntu_24.04/Release.key \
  | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/security_zeek.gpg > /dev/null

sudo apt update && sudo apt install -y zeek

# Add Zeek to PATH
echo 'export PATH=/opt/zeek/bin:$PATH' >> ~/.bashrc
source ~/.bashrc

zeek --version

2. Configure Interface and Logging

# Edit the main node config
sudo tee /opt/zeek/etc/node.cfg > /dev/null <<'EOF'
[zeek]
type=standalone
host=localhost
interface=eth0   # replace with your capture interface
EOF

# Set log rotation and format
sudo tee -a /opt/zeek/etc/zeekctl.cfg > /dev/null <<'EOF'
LogRotationInterval = 3600
LogDir = /var/log/zeek
EOF

# Create log dir
sudo mkdir -p /var/log/zeek
sudo chown zeek: /var/log/zeek

# Disable interface offloading
sudo ethtool -K eth0 rx off tx off sg off tso off gso off gro off lro off

3. Deploy with ZeekControl

# Install (first-time setup)
sudo zeekctl install

# Start
sudo zeekctl start

# Check status
sudo zeekctl status

# Stop
sudo zeekctl stop

# Zeek restarts automatically on crash; check logs:
sudo zeekctl diag

4. Understand the Log Files

Zeek writes tab-separated logs to /var/log/zeek/current/:

ls /var/log/zeek/current/
# conn.log      — every TCP/UDP/ICMP connection
# dns.log       — every DNS query and response
# http.log      — every HTTP request
# ssl.log       — every TLS handshake
# files.log     — every transferred file
# weird.log     — protocol anomalies
# notice.log    — policy-triggered notices
# dpd.log       — dynamic protocol detection results

Read logs with zeek-cut (Zeek’s column selector):

# Top 10 source IPs by connection count
zeek-cut id.orig_h < /var/log/zeek/current/conn.log \
  | sort | uniq -c | sort -rn | head -10

# All DNS queries for a specific domain
zeek-cut ts id.orig_h query answers < /var/log/zeek/current/dns.log \
  | grep "example\.com"

# TLS connections with expired or self-signed certificates
zeek-cut ts id.orig_h id.resp_h server_name validation_status \
  < /var/log/zeek/current/ssl.log \
  | grep -v "ok"

# HTTP requests with non-200 status and large response bodies (potential data exfil)
zeek-cut ts id.orig_h host uri status_code resp_body_len \
  < /var/log/zeek/current/http.log \
  | awk -F'\t' '$6 > 1000000'

5. Write a Custom Detection Script

Zeek scripts are event-driven. This script detects SSH logins from unexpected countries by watching for repeated connection attempts and fires a notice:

# /opt/zeek/share/zeek/site/detect-ssh-scan.zeek

module SSHScan;

export {
    redef enum Notice::Type += {
        SSH_Scanner,
    };
    const scan_threshold = 5 &redef;
    const scan_interval  = 60 sec &redef;
}

global ssh_attempts: table[addr] of count &default=0 &create_expire=1 min;

event ssh_auth_attempted(c: connection, authenticated: bool)
{
    if ( !authenticated )
    {
        local src = c$id$orig_h;
        ++ssh_attempts[src];

        if ( ssh_attempts[src] == scan_threshold )
        {
            NOTICE([
                $note=SSH_Scanner,
                $conn=c,
                $msg=fmt("%s made %d failed SSH attempts in %s",
                         src, ssh_attempts[src], scan_interval),
                $identifier=cat(src),
            ]);
        }
    }
}

Load it in local.zeek:

echo '@load site/detect-ssh-scan' | sudo tee -a /opt/zeek/share/zeek/site/local.zeek
sudo zeekctl deploy

6. Detect DNS Tunneling

DNS tunneling encodes data in DNS queries to exfiltrate traffic or bypass firewalls. Telltale signs: very long query names, high query rate, unusual record types.

# /opt/zeek/share/zeek/site/detect-dns-tunnel.zeek

module DNSTunnel;

export {
    redef enum Notice::Type += {
        Possible_DNS_Tunnel,
    };
}

event dns_request(c: connection, msg: dns_msg, qtype: count, qclass: count)
{
    if ( |c$dns$query| > 50 )
    {
        NOTICE([
            $note=Possible_DNS_Tunnel,
            $conn=c,
            $msg=fmt("Long DNS query (%d chars): %s from %s",
                     |c$dns$query|, c$dns$query, c$id$orig_h),
        ]);
    }
}

7. Integrate with Elasticsearch (ELK)

# Install Filebeat
sudo apt install -y filebeat

# Use Zeek module
sudo filebeat modules enable zeek

# Edit /etc/filebeat/modules.d/zeek.yml
# Set path for each log type:
# - module: zeek
#   connection:
#     enabled: true
#     var.paths: ["/var/log/zeek/current/conn.log"]

sudo filebeat setup --index-management -E output.logstash.enabled=false \
  -E 'output.elasticsearch.hosts=["http://localhost:9200"]'

sudo systemctl enable --now filebeat

Kibana provides a pre-built Zeek dashboard once data flows in.


8. Query Logs with zq (Fast JSON/TSV Tool)

zq (from the Zed project) queries Zeek TSV logs like a database:

# Install
sudo snap install zq || pip3 install zq

# Find all HTTP requests to a specific host
zq -f text 'host=="malicious.example.com" | count()' /var/log/zeek/current/http.log

# Top talkers by bytes sent
zq -f text 'sum(orig_bytes) by id.orig_h | sort -r sum | head 10' \
  /var/log/zeek/current/conn.log

# DNS queries for newly-registered domains (requires threat intel enrichment)
zq -f text 'qtype_name=="A" | count() by query | sort -r count | head 20' \
  /var/log/zeek/current/dns.log

Zeek Package Manager

# Install ZeekPkg
pip3 install zkg
zkg autoconfig

# Install threat intelligence framework
zkg install zeek/zeek-intel-threatbus

# Install JA3 TLS fingerprinting (identify malware by TLS client fingerprint)
zkg install salesforce/ja3

sudo zeekctl deploy

Performance Tuning

Setting File Recommendation
workers node.cfg 1 per 2 CPU cores; increase for >1 Gbps
LogFlushInterval zeekctl.cfg 60s default; reduce to 10s for real-time SIEM
Disable unused analyzers local.zeek redef dpd_buffer_size = 0; on known-quiet networks
AF_PACKET node.cfg lb_method=af_packet for multi-worker packet balancing


Built by theluckystrike — More at zovo.one