Tools

T1040

Zeek (formerly Bro)

A comprehensive guide to Zeek for network security monitoring — installation, conn.log, dns.log, http.log analysis, detection scripts, and integrating Zeek logs into your SIEM pipeline.

View on Graph

What Zeek Is and Why Analysts Use It

  • Zeek (formerly called Bro) is an open-source network security monitoring framework. Unlike Snort/Suricata which focus on signature-based alerting, Zeek’s primary output is structured, application-layer logs that record everything it sees on the wire. Tools like RITA analyze Zeek logs for beacon detection and C2 identification.
  • MITRE ATT&CK maps Zeek’s core function to T1040 (Network Sniffing) — Zeek is a passive network sensor that reads traffic without injecting packets.
  • Analysts use Zeek when they need to answer “what happened on the network” rather than “did a rule fire.” For C2 investigations, lateral movement tracking, and DNS tunneling detection, Zeek logs are often more useful than IDS alerts.
  • Zeek is also highly extensible via scripts (Zeek scripts, Event Language) and can perform real-time analysis: detecting DGA domains, SSH brute force, HTTP anomalies, and protocol violations.

Core Log Files — The 80/20

Zeek produces many log files. These are the ones you will use in every investigation:

Log FileWhat It RecordsKey FieldsAnalyst Use Case
conn.logEvery network connection — TCP, UDP, ICMPts, uid, id.orig_h, id.orig_p, id.resp_h, id.resp_p, proto, service, duration, orig_bytes, resp_bytes, conn_stateFoundation of every investigation — connection timeline, data volume analysis
dns.logEvery DNS query and responsets, uid, id.orig_h, id.resp_h, query, qtype_name, rcode_name, answersC2 domain detection, DNS tunneling, DGA analysis
http.logEvery HTTP request and responsets, uid, id.orig_h, id.resp_h, method, host, uri, referrer, user_agent, status_code, response_body_lenDrive-by download investigation, phishing page access, malicious file download
ssl.logTLS/SSL handshake metadatats, uid, id.orig_h, server_name, cert_chain_fps, subject, issuer, validation_statusJA3(S) hash collection, C2 detection via unusual TLS fingerprints
files.logFile extraction metadatats, fuid, tx_hosts, rx_hosts, mime_type, filename, md5, sha1, sha256Malware analysis — extract files from HTTP, SMTP, FTP sessions
notice.logZeek-generated alerts (from detection scripts)ts, uid, note, msg, sub, dstHigh-priority events generated by Zeek’s built-in or custom scripts
weird.logProtocol anomaliests, uid, name, addl, peerProtocol violations, malformed packets, evasion attempts

Installation and Configuration

Quick Install

# Debian/Ubuntu
sudo apt install zeek

# Or build from source (if you need specific version or custom scripts)
git clone --recursive https://github.com/zeek/zeek.git
cd zeek
./configure && make && sudo make install

Basic Configuration

Zeek’s configuration file is /etc/zeek/zeekctl.cfg (ZeekControl) or /usr/local/zeek/etc/networks.cfg.

# Edit /usr/local/zeek/etc/zeekctl.cfg
# Set the interface to monitor
Interface=eth0

# Edit /usr/local/zeek/etc/networks.cfg to define your local networks
# (used for direction tracking, not for alerting)
10.0.0.0/8
172.16.0.0/12
192.168.0.0/16

# Start Zeek
sudo zeekctl deploy

# Check status
sudo zeekctl status

Reading Zeek Logs — Command Line Quick Start

# View live connections in real time
tail -f /usr/local/zeek/logs/current/conn.log | zeek-cut ts id.orig_h id.resp_h id.resp_p proto service

# Search for connections to a specific IP
cat /usr/local/zeek/logs/current/conn.log | zeek-cut ts id.orig_h id.resp_h id.resp_p proto | grep "185.220.101."

# Extract DNS queries for a specific domain
cat /usr/local/zeek/logs/current/dns.log | zeek-cut ts query answers | grep "evil.com"

# Find HTTP requests to known-bad user agents
cat /usr/local/zeek/logs/current/http.log | zeek-cut ts host uri user_agent | grep -iE "curl|wget|python-requests|powershell"

Detection — Key SPL/KQL Queries Using Zeek Logs

C2 Beacon Detection — conn.log Analysis

C2 beacons produce connections at regular intervals (e.g., every 60 seconds) with similar packet sizes. Use conn.log timing to detect the pattern.

SPL query — beaconing detection:

index=zeek sourcetype=conn.log
| search id.resp_h=*EXTERNAL* AND proto=tcp
| sort id.resp_h, ts
| streamstats time_window=5m count(id.resp_h) as beacon_count by id.resp_h, id.orig_h
| where beacon_count > 5
| table ts, id.orig_h, id.resp_h, id.resp_p, beacon_count, orig_bytes, resp_bytes
| eval alert = "Frequent connections to " . id.resp_h . " from " . id.orig_h . " — possible beaconing"
| sort - beacon_count

DGA Domain Detection — dns.log

DGA (Domain Generation Algorithm) domains are algorithmically generated — they look random, have high entropy, and typically produce NXDOMAIN responses.

SPL query — high-entropy DNS queries with NXDOMAIN:

index=zeek sourcetype=dns.log
| search rcode_name=NXDOMAIN
| eval domain_length = len(query)
| eval entropy = abs(random) % 10  (simplified — real entropy calculation is more complex)
| where domain_length > 20
| stats count by query, id.orig_h
| where count > 10
| eval alert = "DGA-like domain: " . query . " — " . count . " NXDOMAIN responses"
| table query, id.orig_h, count, alert
| sort - count

Malicious File Download — http.log + files.log

Correlating HTTP requests with downloaded files reveals drive-by downloads and payload delivery.

SPL query — suspicious file downloads:

index=zeek sourcetype=http.log
| search mime_type IN ("application/x-msdownload", "application/octet-stream", "application/x-msexcel", "application/vnd.ms-office")
| stats count by id.orig_h, id.resp_h, uri, mime_type
| where count > 1
| eval alert = "Suspicious file download — " . mime_type . " on " . uri
| table id.orig_h, id.resp_h, uri, mime_type, alert

TLS Fingerprinting (JA3) — ssl.log

Malware uses distinct TLS client fingerprints (JA3 hashes). Known-bad JA3 hashes can be matched against Zeek ssl.log.

SPL query — JA3 hash matching:

index=zeek sourcetype=ssl.log
| lookup ja3_blocklist.csv ja3 AS ja3 OUTPUT label
| where label != ""
| eval alert = label . " JA3 fingerprint detected from " . id.orig_h . " to " . id.resp_h . ":" . id.resp_p
| table ts, id.orig_h, id.resp_h, id.resp_p, ja3, server_name, alert

Zeek Scripts — Custom Detection

Zeek’s real power is its scripting language. Here are two essential detection scripts:

SSH Brute Force Detection

# Detect SSH brute force — multiple failed auth attempts in short time
module SSHBruteforce;

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

    const brute_threshold: count = 5;
    const brute_interval: interval = 1min;
}

event ssh_auth_failed(c: connection) {
    local src = c$id$orig_h;
    local dst = c$id$resp_h;
    
    local brute_check = SumStats::create(
        [$name = "ssh_bruteforce",
         $reducer = SumStats::Reducer($stream = SumStats::make_stream(SSH::failed_auth)),
         $epoch = brute_interval,
         $threshold = brute_threshold,
         $result = function(key: SumStats::Key, result: SumStats::Result) {
             local msg = fmt("SSH brute force from %s to %s: %d failed attempts in %s",
                            key$str, dst, result[$stream], brute_interval);
             NOTICE([$note=SSH_Brute_Force, $msg=msg, $src=src, $dst=dst]);
         }
        ]);
}

DNS Tunneling Detection

# Detect DNS tunneling — excessive TXT records, long TXT responses
module DNSTunneling;

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

    const txt_response_threshold: count = 10;
    const txt_response_interval: interval = 1min;
}

event dns_message(c: connection, is_orig: bool, msg: dns_msg, len: count) {
    local src = c$id$orig_h;
    
    SumStats::observe("dns_txt_count", SumStats::key_create(src), SumStats::observe_count(1));
    
    if (SumStats::check_threshold("dns_txt_count", SumStats::key_create(src), txt_response_threshold)) {
        NOTICE([$note=DNS_Tunneling, 
                $msg=fmt("Possible DNS tunneling: %s sent %d+ TXT queries in %s", 
                         src, txt_response_threshold, txt_response_interval),
                $src=src]);
    }
}

Integrating Zeek Logs into Your SIEM

SIEMIntegration MethodNotes
SplunkSplunk Universal Forwarder reading Zeek log filesConfigure inputs.conf for each log file — timestamp extraction works natively
Elastic SecurityFilebeat Zeek modulefilebeat module enable zeek — pre-built parsing for all Zeek log types
Azure SentinelLogstash with zeek input pluginParse Zeek logs into CEF format or custom fields
Custom pipelineVector.dev or Logstash — read from *.log files, transform, shipUse zeek-cut or inline CSV parsing to normalize fields

Sources