Network alerts are the most data-dense: you’re swimming in connection logs, DNS queries, and TLS handshakes trying to find the signal. The network playbook gives you a systematic way through the noise.
The Network Pivot Chain
IDS alert fires
│
├─► Read rule + SID (what behavior triggered?)
│
├─► Zeek conn.log on the 5-tuple
│ (duration, bytes_sent, bytes_recv, connection state)
│
├─► Zeek dns.log
│ (what domain resolved to the flagged IP? reverse lookup?)
│
├─► Zeek ssl.log
│ (JA3/JA4 fingerprint, certificate subject, SNI vs cert mismatch?)
│
├─► Pivot to SOURCE HOST
│ (what process made this connection? Sysmon 3 or EDR)
│
└─► Frequency analysis
(regular interval? = beacon candidate)
Reading Zeek conn.log
The conn.log is your first pivot after an IDS hit. Each row represents one TCP/UDP session.
Key fields to read:
Field What it tells you High-signal values durationSession length in seconds > 300s = long connection; investigate orig_bytesBytes from source to dest High = potential exfil resp_bytesBytes from dest to source High relative to orig = C2 returning data conn_stateSession state S1 (syn only), SF (normal close), RSTO (reset by originator)serviceProtocol detected ”http”, “ssl”, blank (unrecognized protocol) — blank on 443 = suspicious
Worked Example
Suricata hit → Zeek pivot IDS Alert: ET MALWARE Cobalt Strike Beacon (JA3)
5-tuple: 10.1.5.42:49892 → 185.220.101.5:443 (TCP)
Zeek conn.log query: grep "10.1.5.42" conn.log | grep "185.220.101.5"
ts: 1723124400.5 uid: C1a2b3 orig_h: 10.1.5.42 orig_p: 49892
resp_h: 185.220.101.5 resp_p: 443 proto: tcp
duration: 14400.1 orig_bytes: 1024 resp_bytes: 87040
conn_state: SF service: - What this tells you:
duration: 14400 = 4 hours. This session has been open for 4 hours.
service: - = No recognized protocol detected on port 443. Legitimate HTTPS would show service: ssl. This is raw TCP on port 443 (T1571 — Non-Standard Port).
orig_bytes: 1024, resp_bytes: 87040 = Small outbound, large inbound. The host is receiving data (commands) from the C2, not exfiltrating.
Next pivot: ssl.log to confirm JA3 fingerprint, then Sysmon Event 3 to identify the process.
Beacon Math
When you see a pattern of short connections at regular intervals, you may have a beacon. Quantify it before concluding.
Step 1: Extract timestamps of all connections from source to destination.
# Zeek query: all connections from 10.1.5.42 to 185.220.101.5
grep "10.1.5.42" conn.log | grep "185.220.101.5" | awk '{print $1}'
Step 2: Calculate intervals between consecutive connections.
timestamps = [ 1723124400 , 1723124460 , 1723124521 , 1723124579 , ... ]
intervals = [t2 - t1 for t1, t2 in zip (timestamps, timestamps[ 1 :])]
# intervals = [60, 61, 58, 62, 59, ...]
Step 3: Calculate mean and standard deviation.
Mean interval: 60s. Std dev: 1.5s. Coefficient of variation (CV): 2.5%.
Interpretation: CV < 5% = highly regular = strong beacon candidate. CV 5–30% = moderate jitter = possible beacon. CV > 50% = irregular = probably not beaconing.
Cobalt Strike default parameters
Default Cobalt Strike sleep interval: 60 seconds. Default jitter: 0% (no randomization). This makes a 60s±0 pattern a high-confidence Cobalt Strike indicator — though sophisticated operators always change defaults. Common altered profiles: 5–30s sleep with 20–40% jitter, HTTP/HTTPS channels, modified user-agents.
DNS Tunnelling Indicators
DNS tunnelling encodes data in DNS queries and responses to exfiltrate data or establish C2 through a firewall that only allows port 53.
Indicators to check in Zeek dns.log:
Indicator Description Threshold Query length Long subdomains carry encoded data > 40 characters Query entropy Random-looking subdomains Shannon entropy > 3.5 Query volume Many queries per minute to same domain > 10/min Record type NULL and TXT records rarely used legitimately Any NULL or many TXT NXDOMAIN rate DGA generates many non-resolving domains > 20% of queries
Worked Example
DNS tunnelling pattern Zeek dns.log excerpt:
query: aGVsbG8gd29ybGQgdGhpcyBpcw.evil-dns.com (type:TXT)
query: IGEgdGVzdCBvZiBETlMgdHVubg.evil-dns.com (type:TXT)
query: ZWxpbmcgdXNlZCBmb3IgZXhmaWw.evil-dns.com (type:TXT) The subdomains are Base64 encoded (“hello world this is a test of DNS tunnelling used for exfil”). The TXT record type and encoded subdomain are strong indicators. Pivot to: (a) total volume of queries to evil-dns.com in the last 24h, (b) the host making these queries (Sysmon 3 + EDR), (c) VT/DNSDB lookup for evil-dns.com.
TLS Analysis: JA3/JA4 and Certificate Anomalies
JA3 fingerprinting
JA3 hashes the TLS ClientHello message: TLS version, cipher suites, extensions, elliptic curves. Different clients produce different JA3 values. A raw Python socket produces a different JA3 than Chrome, Firefox, or curl. Known malware frameworks have published JA3 fingerprints in threat intel feeds. Finding a known-malware JA3 is a high-confidence hit even without the destination IP being known-bad.
Certificate anomalies to check:
SNI field (server name in ClientHello) ≠ certificate.subject.CN → masquerading
Freshly issued certificate (not-before within 7 days of first connection)
Let’s Encrypt certificate + bulletproof hosting ASN
Self-signed certificate on port 443 at consumer IP
Protocol on port vs port number
A connection to port 443 is not necessarily HTTPS. Zeek’s service field tells you what protocol it actually detected. service: ssl = TLS/HTTPS. service: - = unrecognized protocol — could be raw TCP C2. Legitimate applications don’t use port 443 for raw TCP. When you see 443 + no TLS, that’s the tell.
Zeek Log Reference— Zeek Documentation
JA4+ Fingerprinting— FoxIO
Retrieval Practice
A Suricata alert fires on a JA3 hash associated with Cobalt Strike. Walk through the five-step pivot chain to confirm or refute the alert.
Reveal answer
1. Read the rule — which JA3 value, from which host to which destination? 2. Zeek conn.log — duration (long?), bytes (small outbound, large inbound?), service field (blank = no TLS?). 3. Zeek ssl.log — confirm JA3 match, check certificate subject and SNI for mismatch. 4. Zeek dns.log — what domain resolved to the destination IP? Does it look freshly registered or suspicious? 5. Source host — Sysmon Event 3 or EDR netconn — which process made this connection? That's the payload.
Retrieval Practice
How do you compute beacon jitter, and what coefficient of variation threshold suggests a true beacon vs normal reconnect behavior?
Reveal answer
Calculate the interval between consecutive connections (t2-t1 for all consecutive connection pairs). Compute the mean and standard deviation of those intervals. Coefficient of variation (CV) = std_dev / mean. CV < 5% = very regular = strong beacon. CV 5–30% = moderate jitter (still could be a configured beacon). CV > 50% = irregular = probably normal reconnects or user-driven traffic.