Overview
Endpoint logs tell you what ran. Network logs tell you where it called home. Many attackers — especially APT groups — operate with minimal endpoint footprint but leave distinctive patterns in network traffic: regular beaconing intervals, unusual DNS queries, encrypted tunnels to unexpected destinations.
This page documents network-based hunting techniques that complement endpoint hunting, covering:
- C2 beaconing detection
- DNS tunnelling and exfiltration
- Abnormal outbound traffic patterns
- Encrypted channel abuse (T1573)
- Lateral movement via SMB and WMI
Hunt 1: C2 Beaconing Detection (T1071, T1132)
Hypothesis
A compromised host is communicating with a C2 server on a regular interval. Most C2 frameworks (Cobalt Strike, Metasploit, custom implants) beacon home every few seconds to minutes with a consistent timing pattern.
What Beaconing Looks Like
1
2
3
4
|
10:00:00 host → 185.220.101.47:443 (64 bytes)
10:00:30 host → 185.220.101.47:443 (64 bytes)
10:01:00 host → 185.220.101.47:443 (64 bytes)
10:01:30 host → 185.220.101.47:443 (64 bytes)
|
Consistent size + consistent interval = strong beaconing indicator.
Query — Splunk (flow data / NetFlow / firewall logs)
1
2
3
4
5
6
7
8
9
10
|
index=firewall action=allowed
| bucket _time span=1h
| stats count, stdev(bytes_out) as byte_variance,
stdev(relative_time(_time,"@s")) as time_variance
by src_ip, dest_ip, dest_port
| where count > 50
| where time_variance < 10 /* low jitter = regular timing */
| where byte_variance < 50 /* consistent payload size */
| where NOT (dest_ip IN (known_good_ips))
| sort -count
|
Query — KQL (Microsoft Sentinel / Defender for Endpoint)
1
2
3
4
5
6
7
8
9
10
11
|
DeviceNetworkEvents
| where ActionType == "ConnectionSuccess"
| where not (RemoteIP startswith "10." or RemoteIP startswith "192.168." or RemoteIP startswith "172.")
| summarize
ConnectionCount = count(),
ByteStdDev = stdev(tolong(SentBytes)),
Intervals = make_list(bin(Timestamp, 1m))
by DeviceName, RemoteIP, RemotePort
| where ConnectionCount > 30
| where ByteStdDev < 100
| sort by ConnectionCount desc
|
Jitter Analysis
Advanced C2 frameworks add jitter (random timing variation) to avoid simple interval detection. Hunt for these with a statistical approach:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
import numpy as np
from scipy import stats
def detect_beaconing(timestamps, threshold_cv=0.3):
"""
Coefficient of Variation (CV) < 0.3 indicates
low variance relative to the mean — consistent beaconing.
"""
if len(timestamps) < 10:
return False, None
intervals = np.diff(sorted(timestamps))
mean_interval = np.mean(intervals)
std_interval = np.std(intervals)
cv = std_interval / mean_interval if mean_interval > 0 else 1
is_beaconing = cv < threshold_cv
return is_beaconing, {
'mean_interval_seconds': round(mean_interval, 2),
'cv': round(cv, 4),
'sample_count': len(intervals)
}
|
Hunt 2: DNS Tunnelling (T1071.004, T1048.003)
Hypothesis
An attacker is encoding data in DNS queries to exfiltrate information or tunnel C2 traffic through DNS, bypassing firewall controls that allow outbound DNS.
What DNS Tunnelling Looks Like
Normal DNS queries are short:
1
2
|
query: google.com → A record
query: api.github.com → A record
|
Tunnelled DNS queries are long and often Base32/Base64 encoded:
1
2
3
|
query: aGVsbG8gd29ybGQ.evil-domain.com
query: dGhpcyBpcyBhIHRlc3Q.evil-domain.com
query: c2Vuc2l0aXZlIGRhdGE.evil-domain.com
|
Query — Splunk (DNS logs)
1
2
3
4
5
6
7
8
9
10
|
index=dns
| eval query_length=len(query)
| eval subdomain_labels=mvcount(split(query,"."))
| where query_length > 50 OR subdomain_labels > 5
| stats count, avg(query_length) as avg_query_len,
dc(query) as unique_queries
by src_ip, domain
| where unique_queries > 20
| where avg_query_len > 40
| sort -unique_queries
|
Entropy Analysis — High Entropy = Encoded Data
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
import math
from collections import Counter
def calculate_entropy(text):
"""Shannon entropy — encoded/random strings score > 3.5"""
if not text:
return 0
freq = Counter(text)
length = len(text)
return -sum((count/length) * math.log2(count/length)
for count in freq.values())
def is_suspicious_dns(query, entropy_threshold=3.5, length_threshold=40):
subdomain = query.split('.')[0]
entropy = calculate_entropy(subdomain)
return {
'query': query,
'entropy': round(entropy, 3),
'length': len(subdomain),
'suspicious': entropy > entropy_threshold and len(subdomain) > length_threshold
}
# Example output:
# {'query': 'aGVsbG8gd29ybGQ.evil.com', 'entropy': 4.12, 'length': 24, 'suspicious': True}
# {'query': 'google.com', 'entropy': 2.58, 'length': 6, 'suspicious': False}
|
Additional Signals
- Single domain receiving hundreds of unique subdomains from one host
- DNS queries to domains registered < 30 days ago (correlate with WHOIS)
- DNS over non-standard ports (port 5353, 8053, 53/UDP to unexpected IPs)
- TXT record queries — commonly abused for C2 data delivery
Hunt 3: Data Exfiltration via HTTPS (T1048.002)
Hypothesis
An attacker is exfiltrating data over HTTPS to a cloud storage service or attacker-controlled server, blending in with normal web traffic.
Volume Anomaly Detection
1
2
3
4
5
6
7
8
9
|
index=proxy OR index=firewall
| where dest_port=443
| bucket _time span=1d
| stats sum(bytes_out) as daily_upload by src_ip, dest_domain
| eventstats avg(daily_upload) as avg_upload, stdev(daily_upload) as std_upload
by src_ip, dest_domain
| where daily_upload > (avg_upload + (3 * std_upload))
| where daily_upload > 50000000 /* > 50MB in a day */
| sort -daily_upload
|
Suspicious Upload Destinations
1
2
3
4
5
6
7
8
9
10
|
index=proxy
| where bytes_out > 10000000 /* > 10MB per session */
| where NOT (dest_domain IN (
"*.microsoft.com", "*.office365.com", "*.sharepoint.com",
"*.google.com", "*.dropbox.com", "*.box.com"
))
| stats sum(bytes_out) as total_uploaded, count as sessions
by src_ip, dest_domain
| where sessions > 3
| sort -total_uploaded
|
Long Connection Duration
Short HTTPS connections are normal. Hours-long connections to unknown IPs are not.
1
2
3
4
5
6
|
index=firewall action=allowed dest_port=443
| eval duration_minutes=duration/60
| where duration_minutes > 60
| where NOT (dest_ip IN (known_cdn_ips))
| table _time, src_ip, dest_ip, dest_port, bytes_out, duration_minutes
| sort -duration_minutes
|
Hunt 4: Lateral Movement via SMB (T1021.002)
Hypothesis
An attacker with valid credentials is spreading through the network using SMB, accessing admin shares (C$, ADMIN$, IPC$) on multiple hosts.
Query — Splunk
1
2
3
4
5
6
|
index=wineventlog EventCode=5140
ShareName IN ("\\\\*\\C$", "\\\\*\\ADMIN$", "\\\\*\\IPC$")
| stats dc(ComputerName) as targets_accessed, values(ComputerName) as targets
by SubjectUserName, IpAddress
| where targets_accessed > 3
| sort -targets_accessed
|
Correlate with Login Failures
A sweep preceded by login failures indicates credential bruteforcing before successful lateral movement:
1
2
3
4
5
6
|
index=wineventlog EventCode IN (4625, 5140)
| eval event_type=if(EventCode==4625, "failed_login", "smb_access")
| stats values(event_type) as events, dc(ComputerName) as targets
by SubjectUserName, IpAddress
| where (events="failed_login" AND events="smb_access")
| sort -targets
|
Hunt 5: Anomalous DNS Resolutions Before Network Connections
Hypothesis
Malware often resolves a C2 domain immediately before making a connection. Correlating DNS queries with subsequent connections reveals implants that use DGA (Domain Generation Algorithms) or freshly registered domains.
1
2
3
4
5
6
7
8
9
|
index=dns
| join type=inner src_ip
[search index=firewall action=allowed
| rename dest_ip as resolved_ip]
| where dns_query_time < connection_time
| where (connection_time - dns_query_time) < 5
| eval domain_age=now() - domain_registration_date
| where domain_age < 2592000 /* Domain registered < 30 days ago */
| table _time, src_ip, query, resolved_ip, dest_port
|
Building a Network Hunt Programme
| Frequency |
Hunt Type |
Data Source |
| Daily |
Beaconing anomalies (new destinations) |
Firewall / NDR |
| Daily |
DNS entropy spike |
DNS resolver logs |
| Weekly |
Large outbound transfers |
Proxy / firewall |
| Weekly |
SMB lateral movement sweep |
Windows Security Events |
| Monthly |
Full port scan of internal east-west traffic |
NetFlow |
Contact me at contact@malsayegh.ae to discuss building a network hunting programme for your environment.