How to Detect C2 Beaconing Traffic in Your Network Logs

Retool's new app builder is where AI-generated code ships safely
Building apps with AI is easy. Getting them to production safely is another story.
Modern C2 frameworks are designed to blend in. Cobalt Strike, Sliver, Havoc, and Mythic all default to HTTPS on port 443, use legitimate-looking User-Agent strings, and support domain fronting through CDNs like Cloudflare or Amazon. On the wire, a Cobalt Strike beacon to a threat actor's server looks identical to a browser fetching a web page.
Detection requires a different approach: not inspecting payloads (which are encrypted) but analyzing behavioral patterns. Legitimate web traffic is irregular and varied. A beacon is periodic and consistent: the same destination, similar sizes, similar timing: even with jitter applied.
Detection Layer 1: DNS Query Analysis
Every C2 session begins with DNS resolution. Even if the C2 traffic itself is HTTPS, the DNS queries are plaintext and often reveal suspicious patterns.
What to look for in DNS logs:
- High-frequency queries to the same domain from one host:
// Microsoft Sentinel: detect repetitive DNS to same domain
DnsEvents
| where TimeGenerated > ago(24h)
| where QueryType == "A" or QueryType == "AAAA"
| where Name !endswith ".microsoft.com"
and Name !endswith ".windows.com"
and Name !endswith ".googleapis.com"
and Name !endswith ".akamaiedge.net"
and Name !endswith ".cloudfront.net" // Extend exclusions for your env
| summarize
QueryCount = count(),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated)
by Computer, Name
| where QueryCount > 50 // 50+ queries to same domain in 24h is unusual
| extend Duration = LastSeen - FirstSeen
| where Duration > 1h // Spread over more than 1 hour (not a one-time burst)
| sort by QueryCount desc
- DGA (Domain Generation Algorithm) detection: many unique domains: Many C2 frameworks use DGA to generate many seemingly random domain names. The malware and the C2 server both run the same algorithm, so only the "correct" domain resolves:
// Hosts querying many unique high-entropy domains in a time window
DnsEvents
| where TimeGenerated > ago(1h)
| extend DomainLength = strlen(Name)
| extend SLD = tostring(split(Name, ".")[-2]) // Second-level domain
| summarize
UniqueDomainsQueried = dcount(Name),
AvgDomainLength = avg(DomainLength)
by Computer, bin(TimeGenerated, 1h)
| where UniqueDomainsQueried > 100 // Unusually many unique domains in 1 hour
and AvgDomainLength > 12 // Long random-looking domain names
| sort by UniqueDomainsQueried desc
- Newly registered domains (NRD): Most C2 infrastructure uses newly registered domains: legitimate services have been registered for months or years. Query domain registration age:
# Bulk domain age check using Zeek + whois
# Or use a threat intel feed that includes domain registration dates:
# ThreatFox, URLhaus, Pulsedive all include NRD indicators
# Cisco Umbrella and Cloudflare Gateway have built-in NRD blocking
Detection Layer 2: Proxy and Flow Log Beaconing Analysis
Proxy logs (if you have a web proxy) and NetFlow logs reveal the timing regularity that characterizes beaconing.
Splunk SPL: beaconing detection via connection timing:
/* Proxy logs: find hosts with periodic connections to the same destination */
index=proxy
| eval epoch=_time
| stats
values(epoch) as timestamps
count as conn_count
avg(bytes) as avg_bytes
by src_ip, dest_host
| where conn_count > 10
| eval
/* Calculate standard deviation of inter-arrival times */
sorted_ts=mvdedup(timestamps)
inter_arrival_ms=mvrange(0, mvcount(sorted_ts)-1, 1)
/* This is simplified: production use should calculate actual std dev */
| where avg_bytes < 10000 /* Small payloads typical of beacons */
| where conn_count > 20
| sort -conn_count
| table src_ip, dest_host, conn_count, avg_bytes
Python-based beaconing detector (run against proxy or flow logs):
import pandas as pd
import numpy as np
from scipy import stats
def detect_beacons(df: pd.DataFrame,
time_col='timestamp',
src_col='src_ip',
dst_col='dst_host',
min_connections=10) -> pd.DataFrame:
"""
Detect beaconing by finding src/dst pairs with low standard deviation
in inter-connection intervals.
"""
results = []
for (src, dst), group in df.groupby([src_col, dst_col]):
if len(group) < min_connections:
continue
# Sort by time and calculate intervals between connections
times = sorted(group[time_col].astype(float))
intervals = np.diff(times) # seconds between each connection
if len(intervals) < 5:
continue
mean_interval = np.mean(intervals)
std_interval = np.std(intervals)
# Low coefficient of variation = high regularity = likely beacon
cv = std_interval / mean_interval if mean_interval > 0 else float('inf')
results.append({
'src': src,
'dst': dst,
'connection_count': len(group),
'mean_interval_sec': round(mean_interval),
'std_interval_sec': round(std_interval),
'coefficient_of_variation': round(cv, 3),
'beacon_score': 1 - min(cv, 1) # Higher = more regular = more suspicious
})
results_df = pd.DataFrame(results)
# Filter for suspicious beacons: regular timing, many connections
suspicious = results_df[
(results_df['coefficient_of_variation'] < 0.25) & # < 25% variation
(results_df['connection_count'] > 20)
].sort_values('beacon_score', ascending=False)
return suspicious
# Usage with proxy logs:
df = pd.read_csv('proxy_logs.csv', parse_dates=['timestamp'])
df['timestamp'] = df['timestamp'].astype(int) // 10**9 # Convert to Unix seconds
beacons = detect_beacons(df)
print(beacons.head(20))
NetworkMiner and Zeek for packet-level analysis:
# Zeek: extract connection summary for beaconing analysis
# Zeek's conn.log records all TCP/UDP connections with timing, size, and state
zeek -r capture.pcap /opt/zeek/share/zeek/policy/protocols/conn/known-hosts.zeek
# Analyze conn.log for periodic connections:
cat conn.log | zeek-cut ts id.orig_h id.resp_h id.resp_p duration orig_bytes resp_bytes | \
awk '{print $2, $3, $4, $1}' | sort | \
awk 'prev==$1$2$3 {print $3, $4-prevt} {prev=$1$2$3; prevt=$4}' | \
awk '{sum+=$2; count++; if(count%10==0) print "avg:", sum/count, "sec"}'
Briefings like this, every morning before 9am.
Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.
Detection Layer 3: Process-to-Network Correlation
The most definitive C2 detection combines network data with EDR data: which process on the endpoint is making the beaconing connections? Legitimate processes have expected network behavior: unusual network connections from unexpected processes are high-confidence indicators.
Sysmon Event ID 3 (network connection): process-to-network mapping:
// Sentinel: find processes making many connections to external IPs
SysmonEvent
| where EventID == 3 // NetworkConnect
| where not(DestinationIp matches regex @"^(10\.|172\.(1[6-9]|2\d|3[01])\.|192\.168\.|127\.)") // External IPs
| where DestinationPort == 443 or DestinationPort == 80
// Unusual processes making external HTTPS connections:
| where Image !has_any (
"\\chrome.exe", "\\firefox.exe", "\\msedge.exe", "\\iexplore.exe",
"\\outlook.exe", "\\teams.exe", "\\onedrive.exe", "\\msteams.exe",
"\\curl.exe", "\\wget.exe" // Legitimate tools
)
| summarize
ConnectionCount = count(),
UniqueDestinations = dcount(DestinationIp),
DestinationSample = make_set(DestinationIp, 5)
by Computer, Image, bin(TimeGenerated, 1h)
| where ConnectionCount > 10 and UniqueDestinations < 3 // Many connections to few destinations
| sort by ConnectionCount desc
High-suspicion process/network combinations:
// Processes that should never make internet connections
SysmonEvent
| where EventID == 3
| where not(DestinationIp startswith "10."
or DestinationIp startswith "192.168."
or DestinationIp startswith "127."
or DestinationIp startswith "172.")
| where Image has_any (
"\\powershell.exe",
"\\cmd.exe",
"\\wscript.exe",
"\\cscript.exe",
"\\mshta.exe",
"\\regsvr32.exe",
"\\rundll32.exe",
"\\svchost.exe" // Alert if svchost connects to unexpected external IPs
)
| project TimeGenerated, Computer, Image, DestinationIp, DestinationPort, DestinationHostname
// These combinations are high-confidence malicious
Cobalt Strike specific IOCs:
# Cobalt Strike malleable C2 profiles often contain characteristic patterns:
# - HTTPS with specific URI patterns (/jquery-3.3.1.slim.min.js, /updates.rss)
# - Specific HTTP headers (X-Forwarded-For: with internal IP)
# - JA3 TLS fingerprint: 72a7c13d19aad26 (Cobalt Strike default)
# JA3 fingerprinting from Zeek:
# ssl.log contains ja3 field: compare against known Cobalt Strike JA3 hashes
grep -f cobalt_strike_ja3_hashes.txt ssl.log | awk '{print $5, $6, $7}'
# Cobalt Strike JARM fingerprint (active fingerprinting of TLS server):
# python3 jarm.py <target-ip> <port>
# Compare against known Cobalt Strike JARM fingerprints
Threat Hunting Workflow: Putting It Together
A structured C2 beaconing hunt runs through the three detection layers in sequence, correlating findings across layers for confidence:
Step 1: Identify candidates from DNS/proxy (low specificity, many results)
- Hosts querying the same external domain >50 times per day
- Hosts with connections to destinations with low Alexa/Majestic rank (newly registered or obscure)
- Hosts showing statistical regularity in connection timing
Step 2: Correlate with process data (raises confidence significantly)
- For each suspicious host/destination pair, check which process is making the connections
- Is it a browser? (likely benign): Is it an Office application? (suspicious): Is it cmd.exe or PowerShell? (high confidence malicious)
Step 3: Enrich the destination with threat intel
# Check each suspicious destination against multiple intel feeds
curl -s https://pulsedive.com/api/?indicator=suspicious-domain.com
# VirusTotal API
curl -H 'x-apikey: YOUR_KEY' https://www.virustotal.com/api/v3/domains/suspicious-domain.com
# Shodan: what is hosted on this IP?
curl -s https://api.shodan.io/shodan/host/1.2.3.4?key=YOUR_KEY
# Domain age check
whois suspicious-domain.com | grep 'Creation Date'
Step 4: Triage and escalate
- High confidence (suspicious process + regular timing + low-rep destination + fresh domain) → P1 incident
- Medium confidence (regular timing + low-rep domain, but browser process) → add to watchlist, continue hunting
- Low confidence (single signal) → tune detection or add to watchlist
RITA (Real Intelligence Threat Analytics): automated beaconing detection:
# RITA is an open-source tool that analyzes Zeek logs for beaconing, DNS anomalies, and data exfiltration
git clone https://github.com/activecm/rita
# Import Zeek logs:
rita import /path/to/zeek/logs /dataset/name
# Analyze for beacons:
rita show-beacons /dataset/name | head -20
# Output: score, src, dst, connection_count, avg_interval_sec
The bottom line
C2 beacons are statistical anomalies, not signature matches: detection requires analyzing timing regularity and behavioral patterns across DNS, proxy, and flow logs. Start with DNS: repetitive queries to the same low-reputation domain from one host. Layer proxy/flow analysis: regular intervals (low coefficient of variation) and small consistent payload sizes. Correlate with EDR: which process is making the connections? Processes that should not make internet connections (PowerShell, cmd.exe, mshta.exe) making periodic external HTTPS connections is high-confidence C2 beaconing. RITA automates the beacon scoring analysis against Zeek log collections.
Frequently asked questions
How do you detect C2 beaconing in network traffic?
Analyze DNS logs for hosts making repetitive queries to the same low-reputation domain (>50 times per day). Analyze proxy or flow logs for statistical regularity: connections with low coefficient of variation in inter-arrival times (< 25% variation) are beacons, not random web traffic. Correlate with EDR process-to-network data: which process is making the connections? Unusual processes (PowerShell, cmd.exe, mshta.exe) making periodic external HTTPS connections are high-confidence C2 indicators.
What is C2 beaconing?
C2 beaconing is the periodic outbound communication from malware on a compromised host to the attacker's command-and-control server. Modern C2 frameworks (Cobalt Strike, Sliver, Havoc) disguise beaconing as HTTPS traffic on port 443 with configurable intervals and jitter. Detection relies on behavioral analysis: statistical regularity in connection timing and payload sizes: rather than payload inspection, which is blocked by TLS encryption.
What is Cobalt Strike and how do defenders detect it?
Cobalt Strike is a commercial penetration testing framework that is widely abused by ransomware groups, APTs, and cybercriminals for post-exploitation C2 operations. Its Beacon implant communicates with C2 servers via HTTP/HTTPS, DNS, or SMB using highly configurable malleable C2 profiles that can masquerade as legitimate traffic (Google Analytics, Microsoft Teams, CDN traffic). Detection signatures: JARM TLS fingerprints for Cobalt Strike team servers (published by Salesforce/Fastly), network patterns of regular HTTPS beaconing to domains with recent registration or mismatched certificates, and Sysmon Event ID 10 (process access) showing LSASS access from processes associated with CS activity. CISA has published dedicated guidance on detecting Cobalt Strike.
How do I use network traffic analysis to detect C2 communication?
C2 detection via network analysis focuses on four characteristics: (1) Connection regularity — run statistical analysis on connection frequency to external destinations; C2 beacons appear as highly regular intervals even with jitter. (2) Beacon source processes — correlate network connections with process data; cmd.exe, regsvr32.exe, and LOLBins making regular HTTPS connections are high-confidence C2 indicators. (3) DNS analysis — C2 via DNS shows unusual query volumes to recently registered domains or domains with high entropy names. (4) Domain categorization — check outbound connections against threat intelligence feeds for known bad domains and uncategorized domains (malware uses infrastructure that legitimate businesses do not). Zeek (formerly Bro) is the preferred tool for generating structured network telemetry for these analyses.
What is domain fronting in C2 communication and how is it detected?
Domain fronting routes C2 traffic through a CDN (Cloudflare, AWS CloudFront, Azure CDN) to hide the real C2 server. The malware connects to a legitimate CDN IP address (which appears in network logs), but the HTTP Host header specifies the attacker's actual domain — the CDN routes the request to the C2 server based on the Host header. Network monitoring sees only legitimate CDN IPs and cannot identify the final destination. Detection: inspect the SNI (Server Name Indication) field in TLS ClientHello packets and the HTTP Host header after TLS termination (requires TLS inspection proxy); alert on mismatches between the SNI domain and the HTTP Host header. Many CDN providers have disabled domain fronting: AWS CloudFront and Azure CDN no longer allow it, but other CDNs may still permit it.
How do I tune a beaconing detection rule to reduce false positives from legitimate software?
The highest-volume false positives in beaconing detection come from software update agents (Windows Update, CrowdStrike, Carbon Black, Tanium), telemetry services (Microsoft 365 Apps, Google Chrome), and backup agents -- all of which make periodic connections at regular intervals. Build an exclusion list by running your detection query in a dry-run mode for one week and manually reviewing the top 50 flagged host/destination pairs. For each pair, cross-reference the destination domain against business-owned or commercially recognized IP ranges, check which process owns the connection via EDR telemetry, and add confirmed-benign pairs to a suppression allowlist keyed on the combination of source process name and destination FQDN rather than just the destination IP (which may change). Set a minimum connection count threshold of at least 20 connections and a minimum duration of four hours before alerting -- one-time bursts from update checks look like beacons statistically but are not. Residual false positives after tuning should be handled with a watchlist approach: flag and enrich rather than alert until additional signals are present.
Sources & references
Free resources
Critical CVE Reference Card 2025–2026
25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.
Ransomware Incident Response Playbook
Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.
Get threat intel before your inbox does.
50,000+ security professionals read Decryption Digest for early warnings on zero-days, ransomware, and nation-state campaigns. Free, daily, no spam.
Unsubscribe anytime. We never sell your data.

Founder & Cybersecurity Evangelist, Decryption Digest
Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.
Win a $2,495 Black Hat pass.
Full-access to Black Hat USA 2026 in Las Vegas. Subscribe free to enter.
