Threat Hunting Hypotheses: How to Build Better Hunts
#Your Threat Hunts Are Failing Because Your Questions Are Broken#link
A threat hunt is not a fishing expedition. You do not dump three terabytes of Sysmon logs into a data lake, run a clustering algorithm, and wait for evil to float to the top. That is alert fatigue wearing a different hat. A real hunt starts with a specific, falsifiable claim about your environment—a hypothesis—and ends with either proving it wrong or catching something your SIEM missed. In 2023, when Volt Typhoon was pre-positioning inside critical infrastructure networks using living-off-the-land binaries (LOLBins), organizations that ran generic "look for suspicious PowerShell" hunts found nothing. The teams that caught them were asking a much narrower question: "Is any process spawning netsh.exe to create port proxies from an unexpected parent?"
TL;DR 1. A valid hunting hypothesis must be falsifiable, scoped to a specific MITRE ATT&CK technique, and tied to a log source you actually collect. 2. Broad questions like "Are we compromised?" are not hypotheses; they are panic. Structure them as "If [adversary behavior], then [observable artifact] in [specific log]." 3. The most common reason hunts fail is not bad query syntax—it is querying for artifacts that your telemetry stack is configured to drop or ignore. 4. Every hunt must end with a concrete output: a new detection rule, a confirmed gap in logging, or a documented false positive baseline. If it ends with "nothing found" and no documentation, the hunt didn't happen.
The Anatomy of a Testable Claim
A hypothesis is a bridge between threat intelligence and your local telemetry. It translates what an adversary does in the abstract into what they leave behind on your specific disk drives and network taps. The difference between a junior analyst's hunt and a senior hunter's hunt is rarely their KQL or SPL syntax; it is the precision of the question they ask before they touch the keyboard.
Consider the mechanics of credential access via LSASS memory dumping. A weak hypothesis states: "Adversaries might be dumping credentials." This is useless. It doesn't tell you what to query, what time window to look at, or what a true positive looks like. A strong hypothesis follows a strict conditional structure: "If an adversary is using MiniDumpWriteDump via a custom binary to access LSASS (T1003.001), then we should observe processes other than taskmgr.exe requesting PROCESS_VM_READ and PROCESS_QUERY_INFORMATION access rights to lsass.exe in our Sysmon Event ID 10 logs."
The mechanism matters. Why does T1003.001 generate Event ID 10? Because to read another process's memory without injecting code (which EDRs easily catch), tools like Mimikatz or NanoDump must open a handle to the target process with specific memory-read permissions. Sysmon monitors cross-process handle creation. If you don't understand the Windows API call underneath the ATT&CK technique, you will write a query that looks for the wrong thing.
Why Generic Hunts Miss Real Intrusions
The security industry has a bad habit of publishing "Top 10 Threat Hunting Queries" blog posts that amount to searching for known bad file hashes or grepping for the string "mimikatz". These are not hunts. They are retroactive IOC sweeps. By definition, if you are hunting for a static hash, you are looking for an adversary who hasn't changed their tooling since the report was published. Real adversaries compile their own loaders, rename their binaries, and use native OS utilities.
Let's compare two approaches to hunting for persistence via WMI Event Subscriptions (T1546.003). The generic approach searches for any execution of mofcomp.exe or wmic.exe. In a mid-sized enterprise, this returns thousands of events daily from legitimate software updaters and SCCM agents. The analyst drowns in false positives and abandons the hunt. The comparative, hypothesis-driven approach recognizes that while creating a WMI subscription is normal, creating a permanent WMI event consumer that executes an encoded PowerShell payload upon system startup is not. The query shifts from filtering on the executable name to correlating WMI object creation (Sysmon Event 19, 20, 21) with suspicious command-line arguments in the child process.
| Component | Weak Hypothesis (Untestable) | Strong Hypothesis (Falsifiable) |
|---|---|---|
| Scope | Look for lateral movement | Look for PsExec-style named pipe creation over SMB |
| Behavior | Adversaries move laterally | Adversary uses T1021.002 (SMB/Windows Admin Shares) |
| Observable | Suspicious network traffic | Process accessing \\*\ADMIN$ followed by service creation (Event 7045) |
| Log Source | Network logs | Sysmon Event 1 + Windows Security Event 5140 + System Event 7045 |
| Outcome | Analyst gets overwhelmed and stops | Query returns 14 results; 12 are admins, 2 require investigation |
Do not build hypotheses around behaviors your EDR intentionally suppresses. Many endpoint agents filter out high-volume, low-fidelity events by default to save CPU. If your hypothesis relies on seeing every registry modification under HKCU\Software\Microsoft\Windows\CurrentVersion\Run, but your agent only logs modifications to HKLM, your hunt will return zero results and give you a false sense of security. Always verify your telemetry schema before writing the query.
Building the Query: From Theory to Syntax
Once the hypothesis is locked down, translating it into a query requires mapping the behavioral claim to the exact field names in your SIEM. Let's work through a real scenario: hunting for Kerberoasting (T1558.003). The hypothesis is: "If an adversary is requesting Kerberos Ticket Granting Service (TGS) tickets for service accounts to crack offline, then we should see a single source IP requesting RC4-encrypted TGS tickets (encryption type 0x17) for multiple distinct Service Principal Names (SPNs) within a short time window."
Why RC4? Because modern Active Directory environments default to AES-256 for Kerberos encryption. However, if a service account's password is old enough, or if the SPN is misconfigured, an attacker can explicitly request an RC4-HMAC ticket, which is computationally trivial to crack compared to AES. Normal user activity almost never generates a burst of RC4 TGS requests across multiple SPNs. That is the mechanical anomaly we are targeting.
-- Kusto Query Language (KQL) for Microsoft Sentinel / Advanced Hunting// Hypothesis: Kerberoasting via RC4 TGS Requests (T1558.003)// Log Source: SecurityEvent (Event ID 4769 - Kerberos Service Ticket Request)SecurityEvent| where EventID == 4769| where TicketEncryptionType == "0x17" // 0x17 = RC4-HMAC| where Status == "0x0" // Only look at successful requests| extend TargetUserName = tostring(split(TargetUserName, "@")[0])| summarizeUniqueSPNs = dcount(TargetUserName),SPNList = make_set(TargetUserName, 10),StartTime = min(TimeGenerated),EndTime = max(TimeGenerated)by IpAddress, Account| where UniqueSPNs >= 5 // Threshold: 5+ distinct SPNs from one source| project StartTime, EndTime, IpAddress, Account, UniqueSPNs, SPNList| order by UniqueSPNs desc
The threshold of 5 unique SPNs in this query is not a magic number pulled from thin air. It is a baseline. Before running this in production, you must run it against historical data to establish what your environment's normal administrative behavior looks like. If your vulnerability scanners routinely request 10 RC4 tickets during authenticated scans, your threshold needs to be higher, or you need to exclude the scanner's IP range. A hypothesis without environmental baselining just generates noise.
The Telemetry Trap: When Zero Results Means Blindness
You wrote a perfect hypothesis. You mapped it to the correct ATT&CK technique. You wrote an elegant query. You ran it, and it returned zero results. Do you celebrate? No. You assume you are blind until proven otherwise. The most dangerous outcome in threat hunting is a false negative caused by missing telemetry masquerading as a clean environment.
Take process injection via APC (Asynchronous Procedure Call) queue manipulation (T1055.004). To detect this natively without an EDR doing heavy lifting, you need Sysmon Event ID 8 (CreateRemoteThread) combined with specific API monitoring, or ETW (Event Tracing for Windows) providers tracking NtQueueApcThread. If your organization disabled Sysmon Event ID 8 because it generated too many logs from a legacy application, your hunt for APC injection will always return zero. The hypothesis wasn't wrong; the lens you looked through was painted black.
# Verify your Sysmon configuration actually captures what your hypothesis needs# Run this on a test endpoint to check if Event ID 8 is enabledsysmon -c | grep -i "createremotethread"# Or check the active XML config directlysysmon -c > current_sysmon_config.xmlcat current_sysmon_config.xml | grep -A 2 "<RuleGroup groupRelation=\"or\">"# Expected output if enabled:# <RuleGroup name="" groupRelation="or"># <CreateRemoteThread onmatch="exclude">
Closing the Loop: The Hunt Is Not the Output
A hunt that ends with "we didn't find anything" and closes the Jira ticket is a waste of engineering hours. The value of a threat hunt is not solely in catching an active intruder. The value is in the structural improvements forced by the attempt. Every hypothesis tested must yield one of three concrete artifacts.
- ▪A new detection rule: If the query found true positives, tune it, remove the false positives, and promote it to a real-time SIEM alert. The hunt is now automated defense.
- ▪A documented telemetry gap: If the query failed because the logs weren't there, the output is a formal request to the platform engineering team to enable the missing Event ID, ETW provider, or network tap. This is a measurable reduction in risk surface.
- ▪A validated baseline: If the query returned 5,000 benign events, you have just mapped the normal operational footprint of that technique in your environment. Document those parameters so future analysts know what 'normal' looks like.