Tools
T1040Zeek (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 File | What It Records | Key Fields | Analyst Use Case |
|---|---|---|---|
| conn.log | Every network connection — TCP, UDP, ICMP | ts, uid, id.orig_h, id.orig_p, id.resp_h, id.resp_p, proto, service, duration, orig_bytes, resp_bytes, conn_state | Foundation of every investigation — connection timeline, data volume analysis |
| dns.log | Every DNS query and response | ts, uid, id.orig_h, id.resp_h, query, qtype_name, rcode_name, answers | C2 domain detection, DNS tunneling, DGA analysis |
| http.log | Every HTTP request and response | ts, uid, id.orig_h, id.resp_h, method, host, uri, referrer, user_agent, status_code, response_body_len | Drive-by download investigation, phishing page access, malicious file download |
| ssl.log | TLS/SSL handshake metadata | ts, uid, id.orig_h, server_name, cert_chain_fps, subject, issuer, validation_status | JA3(S) hash collection, C2 detection via unusual TLS fingerprints |
| files.log | File extraction metadata | ts, fuid, tx_hosts, rx_hosts, mime_type, filename, md5, sha1, sha256 | Malware analysis — extract files from HTTP, SMTP, FTP sessions |
| notice.log | Zeek-generated alerts (from detection scripts) | ts, uid, note, msg, sub, dst | High-priority events generated by Zeek’s built-in or custom scripts |
| weird.log | Protocol anomalies | ts, uid, name, addl, peer | Protocol 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
| SIEM | Integration Method | Notes |
|---|---|---|
| Splunk | Splunk Universal Forwarder reading Zeek log files | Configure inputs.conf for each log file — timestamp extraction works natively |
| Elastic Security | Filebeat Zeek module | filebeat module enable zeek — pre-built parsing for all Zeek log types |
| Azure Sentinel | Logstash with zeek input plugin | Parse Zeek logs into CEF format or custom fields |
| Custom pipeline | Vector.dev or Logstash — read from *.log files, transform, ship | Use zeek-cut or inline CSV parsing to normalize fields |
Related
- Snort and Suricata — detection and response for T1040 techniques
- Wireshark — detection and response for T1040 techniques
- Network Security Basics — detection and response for T1040, T1046 techniques
