Skip to main content
COSMICBYTEZLABS
NewsSecurityHOWTOsToolsStudyTraining
ProjectsChecklistsAI RankingsNewsletterStatusTagsAbout
Subscribe

Press Enter to search or Esc to close

News
Security
HOWTOs
Tools
Study
Training
Projects
Checklists
AI Rankings
Newsletter
Status
Tags
About
RSS Feed
Reading List
Subscribe

Stay in the Loop

Get the latest security alerts, tutorials, and tech insights delivered to your inbox.

Subscribe NowFree forever. No spam.
COSMICBYTEZLABS

Your trusted source for IT intelligence, cybersecurity insights, and hands-on technical guides.

565+ Articles
117+ Guides

CONTENT

  • Latest News
  • Security Alerts
  • HOWTOs
  • Projects
  • Exam Prep

RESOURCES

  • Search
  • Browse Tags
  • Newsletter Archive
  • Reading List
  • RSS Feed

COMPANY

  • About Us
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CosmicBytez Labs. All rights reserved.

System Status: Operational
  1. Home
  2. HOWTOs
  3. Network Traffic Analysis with Zeek: From Deployment to Threat Detection
Network Traffic Analysis with Zeek: From Deployment to Threat Detection
HOWTOIntermediate

Network Traffic Analysis with Zeek: From Deployment to Threat Detection

Deploy Zeek (formerly Bro) on Linux to passively monitor network traffic, generate structured logs, write detection scripts, and forward data to your SIEM for real-time threat hunting.

Dylan H.

Tutorials

April 6, 2026
6 min read

Prerequisites

  • Ubuntu 22.04 or Debian 12 server (or VM) with at least 2 CPU cores and 4 GB RAM
  • A dedicated network interface in promiscuous mode connected to a SPAN/mirror port
  • Basic Linux command-line proficiency
  • Familiarity with TCP/IP networking concepts
  • (Optional) A running ELK Stack or Splunk instance for log forwarding

Introduction

Zeek (formerly known as Bro) is one of the most powerful open-source Network Security Monitors (NSMs) available. Unlike traditional IDS tools that match against signature patterns, Zeek takes a fundamentally different approach: it passively observes network traffic and produces structured, high-fidelity logs covering every protocol conversation it sees — DNS, HTTP, SSL/TLS, SSH, SMTP, and dozens more.

The result is a rich audit trail that security teams can query during incident response, feed into a SIEM for correlation, or use as input to machine-learning pipelines for anomaly detection. Zeek's built-in scripting language lets you write custom detection logic at the semantic layer — "alert when a client downloads more than 50 unique executables in five minutes" — rather than wrestling with raw packet offsets.

This guide walks through installing Zeek on an Ubuntu/Debian sensor, configuring it for your environment, writing your first detection script, and shipping logs to a central SIEM.


Prerequisites

Before starting, ensure the following are in place:

  • Sensor host: Ubuntu 22.04 LTS or Debian 12 with at least 2 cores and 4 GB RAM. Physical hardware or a VM both work; a dedicated host is recommended for production.
  • Mirror/SPAN port: Your switch or hypervisor should mirror the traffic you want to inspect to the sensor's monitoring interface (eth1 in this guide). Zeek only reads traffic — it never injects packets.
  • Root/sudo access on the sensor.
  • Internet access for package installation (or a local mirror).
  • (Optional) Filebeat installed to ship Zeek JSON logs to Elasticsearch/Splunk.

Step 1 — Install Zeek

Add the Official Zeek Repository

Zeek publishes prebuilt packages for major distros. Using the official repo ensures you get the latest stable release.

# Install prerequisites
sudo apt-get update
sudo apt-get install -y curl gnupg2 lsb-release
 
# Add the Zeek GPG key
curl -fsSL https://download.opensuse.org/repositories/security:zeek/xUbuntu_22.04/Release.key \
  | gpg --dearmor \
  | sudo tee /usr/share/keyrings/zeek-archive-keyring.gpg > /dev/null
 
# Add the repository
echo "deb [signed-by=/usr/share/keyrings/zeek-archive-keyring.gpg] \
  https://download.opensuse.org/repositories/security:zeek/xUbuntu_22.04/ /" \
  | sudo tee /etc/apt/sources.list.d/zeek.list
 
# Install Zeek 7.x (LTS)
sudo apt-get update
sudo apt-get install -y zeek

Zeek installs to /opt/zeek/. Add its binaries to your PATH:

echo 'export PATH=/opt/zeek/bin:$PATH' >> ~/.bashrc
source ~/.bashrc
zeek --version   # Should print: zeek version 7.x.x

Step 2 — Configure the Monitoring Interface

Put the Interface into Promiscuous Mode

The monitoring interface must receive all traffic on the segment, not just traffic addressed to it.

# Bring eth1 up in promiscuous mode (immediate, non-persistent)
sudo ip link set eth1 promisc on
ip link show eth1 | grep promisc   # Confirm: PROMISC appears in flags
 
# Disable offloading features that break packet capture accuracy
sudo ethtool -K eth1 rx off tx off gso off gro off lro off tso off

To persist promiscuous mode across reboots, create a systemd network override:

sudo tee /etc/systemd/network/10-eth1.link > /dev/null <<'EOF'
[Match]
Name=eth1
 
[Link]
Promiscuous=yes
EOF
sudo systemctl restart systemd-networkd

Tell Zeek Which Interface to Use

Edit the node configuration file:

sudo nano /opt/zeek/etc/node.cfg

Replace the default [zeek] stanza with:

[zeek]
type=standalone
host=localhost
interface=eth1

For multi-core sensors, enable PF_RING or AF_PACKET workers to distribute load:

[logger]
type=logger
host=localhost
 
[manager]
type=manager
host=localhost
 
[proxy-1]
type=proxy
host=localhost
 
[worker-1]
type=worker
host=localhost
interface=eth1
lb_method=pf_ring
lb_procs=4

Step 3 — Configure Local Networks and Log Settings

Declare Your Internal RFC 1918 Ranges

sudo nano /opt/zeek/etc/networks.cfg

Uncomment and update to match your environment:

# Internal networks — Zeek uses these for direction tagging (originator vs. responder)
10.0.0.0/8          Private RFC 1918 — Class A
172.16.0.0/12       Private RFC 1918 — Class B
192.168.0.0/16      Private RFC 1918 — Class C

Enable JSON Log Output

By default Zeek writes tab-separated logs. JSON makes ingestion into SIEMs significantly easier.

sudo nano /opt/zeek/share/zeek/site/local.zeek

Add the following at the bottom of the file:

# Enable JSON log format
@load policy/tuning/json-logs
redef LogAscii::use_json = T;
 
# Include timestamps in ISO 8601 format
redef LogAscii::json_timestamps = JSON::TS_ISO8601;
 
# Rotate logs every hour instead of the default 24h
redef Log::default_rotation_interval = 1hr;

Step 4 — Deploy and Start Zeek

Use zeekctl (the Zeek control framework) to manage the deployment lifecycle:

# Apply configuration, install scripts, and check for syntax errors
sudo zeekctl deploy
 
# Verify status
sudo zeekctl status

Expected output:

Name         Type       Host          Status    Pid    Started
zeek         standalone localhost     running   12345  06 Apr 11:23:04

Zeek is now capturing traffic. Logs are written to /opt/zeek/logs/current/:

ls -lh /opt/zeek/logs/current/
# conn.log  dns.log  http.log  ssl.log  files.log  weird.log  notice.log  ...

Tail Live Connection Data

tail -f /opt/zeek/logs/current/conn.log | python3 -m json.tool | head -60

Step 5 — Understanding Key Log Files

Log FileContents
conn.logEvery TCP/UDP/ICMP flow: duration, bytes, state
dns.logAll DNS queries and responses, including TTLs and answer records
http.logHTTP requests: URI, method, status code, user-agent, MIME type
ssl.logTLS handshake details: cert CN, cipher suite, validation status
files.logFiles transferred over HTTP, SMTP, etc. — SHA-256 hashes included
x509.logCertificate chain details for every TLS session
notice.logZeek-generated alerts (your scripts write here)
weird.logProtocol anomalies — unexpected or malformed traffic
smtp.logEmail metadata: sender, recipients, subject, attachment names

Step 6 — Write a Custom Detection Script

Zeek's scripting language lets you write event-driven detection logic. Here's a practical example: detect DNS over non-standard ports (a common data-exfiltration and C2 evasion technique).

Create the script:

sudo nano /opt/zeek/share/zeek/site/detect-dns-tunnel.zeek
##! Detect DNS-like traffic on non-standard ports and flag excessively long hostnames.
 
module DNSTunnel;
 
export {
    redef enum Notice::Type += {
        ## Raised when DNS traffic is seen on a non-standard port
        DNS_On_Nonstandard_Port,
        ## Raised when a queried hostname exceeds a suspicious length threshold
        Long_DNS_Hostname,
    };
}
 
# Threshold: hostnames longer than 60 characters are suspicious
const hostname_len_threshold = 60 &redef;
 
event dns_request(c: connection, msg: dns_msg, query: string, qtype: count, qclass: count)
    {
    # Flag DNS queries on ports other than 53
    if ( c$id$resp_p != 53/udp && c$id$resp_p != 53/tcp )
        {
        NOTICE([$note=DNS_On_Nonstandard_Port,
                $conn=c,
                $msg=fmt("DNS request to non-standard port %s from %s",
                         c$id$resp_p, c$id$orig_h),
                $identifier=cat(c$id$orig_h, c$id$resp_h, c$id$resp_p)]);
        }
 
    # Flag unusually long hostnames (common in DNS tunneling)
    if ( |query| > hostname_len_threshold )
        {
        NOTICE([$note=Long_DNS_Hostname,
                $conn=c,
                $msg=fmt("Long DNS hostname (%d chars): %s queried by %s",
                         |query|, query, c$id$orig_h),
                $identifier=cat(c$id$orig_h, query)]);
        }
    }

Load it in local.zeek:

echo '@load site/detect-dns-tunnel' | sudo tee -a /opt/zeek/share/zeek/site/local.zeek

Reload Zeek without dropping existing connections:

sudo zeekctl deploy

Detections now appear in /opt/zeek/logs/current/notice.log.


Step 7 — Ship Logs to a SIEM with Filebeat

Install Filebeat

curl -fsSL https://artifacts.elastic.co/GPG-KEY-elasticsearch \
  | gpg --dearmor | sudo tee /usr/share/keyrings/elastic-keyring.gpg > /dev/null
echo "deb [signed-by=/usr/share/keyrings/elastic-keyring.gpg] \
  https://artifacts.elastic.co/packages/8.x/apt stable main" \
  | sudo tee /etc/apt/sources.list.d/elastic-8.x.list
sudo apt-get update && sudo apt-get install -y filebeat

Configure the Zeek Module

sudo filebeat modules enable zeek
sudo nano /etc/filebeat/modules.d/zeek.yml
- module: zeek
  connection:
    enabled: true
    var.paths: ["/opt/zeek/logs/current/conn.log"]
  dns:
    enabled: true
    var.paths: ["/opt/zeek/logs/current/dns.log"]
  http:
    enabled: true
    var.paths: ["/opt/zeek/logs/current/http.log"]
  ssl:
    enabled: true
    var.paths: ["/opt/zeek/logs/current/ssl.log"]
  notice:
    enabled: true
    var.paths: ["/opt/zeek/logs/current/notice.log"]
  files:
    enabled: true
    var.paths: ["/opt/zeek/logs/current/files.log"]

Edit /etc/filebeat/filebeat.yml to point at your Elasticsearch instance:

output.elasticsearch:
  hosts: ["https://your-elasticsearch:9200"]
  username: "filebeat_writer"
  password: "${ELASTICSEARCH_PASSWORD}"
  ssl.certificate_authorities: ["/etc/ssl/certs/ca.crt"]
sudo systemctl enable --now filebeat
sudo filebeat test output   # Should print: Connection OK

Verification and Testing

Check Zeek is Capturing Traffic

Generate some test traffic, then inspect the logs:

# On another host on your network, make a DNS request and HTTP call
# On the sensor:
grep '"query"' /opt/zeek/logs/current/dns.log | tail -5
grep '"method":"GET"' /opt/zeek/logs/current/http.log | tail -5

Test Your Detection Script

Simulate a long DNS hostname query (safe — just a lookup):

# From any host on the monitored segment:
dig "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.example.com" @8.8.8.8

Then on the sensor:

grep "Long_DNS_Hostname" /opt/zeek/logs/current/notice.log

You should see a JSON entry with note: "DNSTunnel::Long_DNS_Hostname".

Verify Process Health

sudo zeekctl status         # All workers: running
sudo zeekctl diag           # Detailed diagnostics and crash checks
sudo zeekctl check          # Validate script syntax before deploying

Troubleshooting

Zeek shows stopped in zeekctl status

Check the crash log:

sudo zeekctl diag | tail -40
cat /opt/zeek/logs/current/reporter.log   # Script errors surface here

Most common cause: a syntax error in a .zeek script. Run sudo zeekctl check after every script change before deploying.


No traffic in logs despite Zeek running

  1. Verify the SPAN/mirror port is actually delivering packets: sudo tcpdump -i eth1 -c 20
  2. Confirm promiscuous mode: ip link show eth1 — the PROMISC flag must be present.
  3. Check that hardware offloading is disabled: ethtool -k eth1 | grep -E "rx-checksumming|tx-checksumming"

High CPU usage

  • Enable AF_PACKET workers (see Step 2 multi-core config).
  • Reduce log verbosity: comment out heavy modules in local.zeek (e.g., @load policy/protocols/ssl/validate-certs is expensive).
  • Add a BPF capture filter to exclude irrelevant traffic:
# In node.cfg, add to the worker stanza:
# capture_filter=not host 192.168.100.10 and not port 5353

Zeek not generating notice.log

Your script may not be loaded. Confirm:

grep 'detect-dns-tunnel' /opt/zeek/share/zeek/site/local.zeek
sudo zeekctl check   # No output means no errors
sudo zeekctl deploy

Logs not appearing in Elasticsearch

sudo journalctl -u filebeat --since "5 minutes ago"
sudo filebeat test config -c /etc/filebeat/filebeat.yml

Common issues: incorrect Elasticsearch password, certificate mismatch, or the Zeek module pointing to a rotated log path rather than current/.


Summary

You now have a fully operational Zeek sensor that:

  • Passively captures all traffic on your monitored segment via a SPAN/mirror port
  • Generates structured JSON logs for every DNS query, HTTP request, TLS session, and file transfer it observes
  • Runs custom detection logic in Zeek's event-driven scripting language — illustrated with a DNS tunneling detector
  • Ships logs to your SIEM via Filebeat for correlation, dashboarding, and alerting

From here, explore the Zeek package manager (zkg) to install community scripts covering lateral movement, malware C2 patterns, and protocol anomalies:

sudo /opt/zeek/bin/zkg install zeek/corelight/zeek-spicy-ipsec
sudo /opt/zeek/bin/zkg install zeek/sethhall/zeek-community-id
sudo zeekctl deploy

The Community ID script (zeek-community-id) is especially valuable: it adds a standardised flow hash to every Zeek log, letting you correlate a connection across Zeek, Suricata, and your firewall logs using a single identifier.

Pair Zeek with Suricata (covered in the Suricata IDS/IPS Deployment guide) for defence-in-depth: Suricata catches known bad via signatures while Zeek gives you the full audit trail and behavioural detection layer.

#zeek#network-security#threat-detection#siem#nsm#blue-team#packet-analysis#incident-response

Related Articles

Suricata IDS/IPS Deployment: From Install to Active Threat Detection

Deploy Suricata as a full-featured Network Intrusion Detection and Prevention System on Ubuntu. Covers installation, interface capture, Emerging Threats...

10 min read

Invoke SentinelOne Threat Hunt

Proactive threat hunting is essential for identifying sophisticated threats that evade automated detection systems. This script automates the process of...

20 min read

SentinelOne Application Control Policies

Organizations face security risks from unauthorized applications, malware disguised as legitimate software, and shadow IT installations that bypass...

15 min read
Back to all HOWTOs