> Practical Threat Hunting: Finding Attackers Who Are Already Inside_
Threat hunting is the practice of proactively searching your environment for signs of compromise that automated detections missed. The assumption: you're already breached; you just don't know it yet.
The average dwell time before detection is around 200 days. Those 200 days are spent in your environment — hunting is how you reduce that number.
## The mindset shift
Reactive security waits for alerts. Threat hunting inverts this:
- >Start with a hypothesis ("Are attackers using living-off-the-land binaries for persistence?")
- >Query your data to confirm or deny the hypothesis
- >Either find nothing (good, update your baselines) or find something (incident response)
The output of every hunt that finds nothing is still valuable: better baselines and detection logic.
## Data sources: what you need
A hunt is only as good as the data it can search.
| Data Source | What it provides |
|---|---|
| Windows Event Logs | Process creation (4688), logon (4624/4625), service install (7045), scheduled tasks (4698) |
| Sysmon | Enhanced process creation with cmdline, network connections, file creation, registry changes |
| EDR telemetry | Process tree, memory regions, API calls — much richer than Event Logs |
| Network logs | NetFlow, DNS queries, proxy logs — see what machines talk to |
| Authentication logs | AD domain controller events, VPN logs, cloud identity |
| Cloud audit logs | AWS CloudTrail, Azure Activity Log, GCP Audit Logs |
Without Sysmon installed, install it now:
<!-- Sysmon config for threat hunting -->
<Sysmon schemaversion="4.70">
<EventFiltering>
<RuleGroup name="Processes" groupRelation="or">
<ProcessCreate onmatch="include">
<Image condition="end with">powershell.exe</Image>
<Image condition="end with">cmd.exe</Image>
<Image condition="end with">wscript.exe</Image>
<Image condition="end with">cscript.exe</Image>
<Image condition="end with">regsvr32.exe</Image>
<Image condition="end with">mshta.exe</Image>
<Image condition="end with">certutil.exe</Image>
</ProcessCreate>
</RuleGroup>
</EventFiltering>
</Sysmon>## Hypothesis 1: Living-Off-The-Land (LOLBins) abuse
Why: Attackers avoid custom malware by using legitimate Windows binaries (certutil, mshta, regsvr32, wscript) for execution. These often bypass AV.
KQL (for Sentinel/Defender):
// Find certutil used for downloads (common malware dropper technique)
DeviceProcessEvents
| where FileName =~ "certutil.exe"
| where ProcessCommandLine has_any ("-urlcache", "-decode", "-encode", "-split")
| project Timestamp, DeviceName, InitiatingProcessFileName, ProcessCommandLine
| order by Timestamp descSigma rule:
title: Suspicious CertUtil Usage
id: e011a729-98a3-4a2d-95a8-e987e1369c00
status: stable
logsource:
category: process_creation
product: windows
detection:
selection:
Image|endswith: '\certutil.exe'
CommandLine|contains:
- '-urlcache'
- '-decode'
- '-split'
- 'http'
condition: selection
falsepositives:
- Legitimate certificate operations by administrators
level: high
tags:
- attack.defense_evasion
- attack.t1140What to look for:
// PowerShell with encoded commands (common evasion)
DeviceProcessEvents
| where FileName =~ "powershell.exe"
| where ProcessCommandLine has_any ("-enc", "-EncodedCommand", "-ec")
| extend DecodedCmd = base64_decode_tostring(extract(@"-[Ee][Nn][Cc][Oo][Dd][Ee][Dd]?[Cc][Oo][Mm][Mm][Aa][Nn][Dd]?\s+([A-Za-z0-9+/=]+)", 1, ProcessCommandLine))
| project Timestamp, DeviceName, ProcessCommandLine, DecodedCmd
// MSHTA executing remote content
DeviceProcessEvents
| where FileName =~ "mshta.exe"
| where ProcessCommandLine has_any ("http://", "https://", "vbscript:", "javascript:")## Hypothesis 2: Scheduled task and service persistence
Why: Scheduled tasks are the most common persistence mechanism after malware gets execution. Most legitimate tasks are created at software install time, not at 3am.
// New scheduled tasks - look for recent creation from unusual parents
DeviceEvents
| where ActionType == "ScheduledTaskCreated"
| extend TaskInfo = parse_json(AdditionalFields)
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine,
TaskName = tostring(TaskInfo.TaskName),
TaskContent = tostring(TaskInfo.TaskContent)
| where not(InitiatingProcessFileName has_any ("svchost.exe", "taskschd.exe", "mmc.exe"))
// Services created outside business hours or from cmd/powershell
Event
| where Source == "Service Control Manager"
| where EventID == 7045
| where TimeGenerated between (ago(7d) .. now())
| extend ServiceName = tostring(EventData[0]),
ServiceFile = tostring(EventData[3])
| where ServiceFile has_any ("temp", "appdata", "public", "users")## Hypothesis 3: Lateral movement via SMB / WMI / PsExec
Why: After initial access, attackers move to other machines using stolen credentials. PsExec leaves service artifacts; WMI doesn't — so look for both.
// PsExec detection: service named "PSEXESVC" or similar
Event
| where Source == "Service Control Manager" and EventID == 7045
| where RenderedDescription has "PSEXESVC"
| project TimeGenerated, Computer, RenderedDescription
// WMI lateral movement: wmiprvse.exe spawning unusual children
DeviceProcessEvents
| where InitiatingProcessFileName =~ "WmiPrvSE.exe"
| where FileName has_any ("cmd.exe", "powershell.exe", "net.exe", "whoami.exe")
| project Timestamp, DeviceName, FileName, ProcessCommandLine, InitiatingProcessCommandLine
// Authentication from unexpected sources (detect stolen cred use)
SecurityEvent
| where EventID == 4624
| where LogonType == 3 // Network logon
| where AuthenticationPackageName == "NTLM" // NTLM when Kerberos expected
| where not(SubjectUserName == "ANONYMOUS LOGON")
| summarize LogonCount = count() by IpAddress, TargetUserName, Computer
| where LogonCount > 10
| order by LogonCount desc## Hypothesis 4: Credential access
Why: LSASS dumps, Kerberoasting, and DCSync are core attacker techniques. They all generate specific Event IDs.
// LSASS memory reads (Mimikatz signature)
DeviceEvents
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where not(InitiatingProcessFileName has_any ("MsMpEng.exe", "svchost.exe", "csrss.exe", "werfault.exe"))
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine
// DCSync: replication rights used from non-DC source
SecurityEvent
| where EventID == 4662
| where Properties has "1131f6aa-9c07-11d1-f79f-00c04fc2dcd2" // DS-Replication-Get-Changes
or Properties has "1131f6ad-9c07-11d1-f79f-00c04fc2dcd2" // DS-Replication-Get-Changes-All
| where SubjectUserName !endswith "$" // Not a computer account (DCs authenticate as COMPUTER$)
| project TimeGenerated, Computer, SubjectUserName, SubjectDomainName, ObjectName
// Kerberoasting: large volume of TGS requests with RC4
SecurityEvent
| where EventID == 4769
| where TicketEncryptionType == "0x17" // RC4
| where ServiceName !endswith "$" // Not a computer account
| summarize RequestCount = count() by IpAddress, AccountName
| where RequestCount > 5## Hypothesis 5: Data exfiltration
Why: Before leaving or deploying ransomware, attackers compress and exfiltrate data.
// Large data transfers to external IPs
DeviceNetworkEvents
| where RemoteIPType == "Public"
| where Protocol == "Tcp"
| summarize TotalBytesSent = sum(SentBytes) by DeviceName, RemoteIP, RemotePort
| where TotalBytesSent > 500000000 // 500MB
| order by TotalBytesSent desc
// Archive tools running from unusual locations
DeviceProcessEvents
| where FileName has_any ("7z.exe", "rar.exe", "zip.exe", "tar.exe", "compress.exe")
| where FolderPath !has_any ("Program Files", "Windows")
| project Timestamp, DeviceName, FolderPath, ProcessCommandLine
// Cloud sync tools (rclone is commonly used by ransomware groups)
DeviceProcessEvents
| where FileName =~ "rclone.exe" or ProcessCommandLine has "rclone"
| project Timestamp, DeviceName, ProcessCommandLine## Hypothesis 6: DNS-based C2
Why: Many C2 frameworks use DNS to beacon home — it's allowed through almost every firewall and less monitored than HTTP.
// High-frequency DNS queries to same domain (beaconing)
DnsEvents
| where TimeGenerated > ago(24h)
| summarize QueryCount = count() by DeviceName, Computer, Name
| where QueryCount > 100
| join kind=leftouter (
DnsEvents | summarize RequestPerHour = count() by Name, bin(TimeGenerated, 1h)
) on Name
| where Name !has_any ("windows.com", "microsoft.com", "google.com")
| order by QueryCount desc
// Long subdomain names (DNS tunneling)
DnsEvents
| extend SubLen = strlen(Name)
| where SubLen > 50
| where Name !has_any ("amazonaws.com", "cloudfront.net", "akamai")
| project TimeGenerated, DeviceName, Name, SubLen## Building a hunt playbook
Each hunt should be documented for repeatability:
## Hunt: LOLBin Abuse via CertUtil
**Hypothesis:** Attackers are using certutil.exe to download payloads from the internet
**MITRE:** T1140 (Deobfuscate/Decode Files), T1105 (Ingress Tool Transfer)
**Data required:** Windows process creation logs (Event 4688 with cmdline, or Sysmon Event 1)
**Query:** [paste KQL/SPL/Sigma rule]
**What's normal:** sysadmins using certutil for certificate management
**What's suspicious:** -urlcache -split with HTTP URLs, downloading to Temp/AppData
**Time to run:** 15 minutes
**Last run:** 2025-11-30
**Result:** No findings — baseline updated## Tools
| Tool | Use |
|---|---|
| Chainsaw | Fast Windows Event Log analysis for incident response |
| Hayabusa | Sigma rule-based threat hunting over Windows logs |
| Velociraptor | DFIR and hunting at scale — agent-based collection |
| OSQuery | SQL-like queries against live system state |
| CrowdStrike Falcon / Microsoft Defender | EDR with built-in hunting UI |
| MITRE ATT&CK Navigator | Layer coverage and planning |
# Chainsaw: hunt for common attack patterns in EVTX files offline
chainsaw hunt /path/to/evtx/ -s /sigma/rules/ --mapping /chainsaw/mappings/sigma-event-logs.ymlThreat hunting is a perishable skill — your value is knowing what normal looks like so anomalies stand out. Spend time in your environment's data on quiet days, not just incident days. The attacker who's been in for 90 days leaves subtle traces that only someone who knows the baseline will notice.