Lesson 3 of 5 · 25 min read

Hypothesis Failure Modes — Why Hunts Miss and How to Fix Them

A hunt that misses the adversary is worse than no hunt — it creates false confidence. Knowing the failure modes is as important as knowing the hunting techniques. Every missed hunt has a root cause; most root causes are in the hypothesis or the data source, not in the analysis.


The Taxonomy of Hunt Failure Modes

Hunt Failure
├── Hypothesis failure
│   ├── Too broad (candidate flood)
│   ├── Too narrow (excludes valid attacker variations)
│   ├── Unfalsifiable (no success/failure criteria)
│   ├── Premature closure (stopped before exhausting evidence)
│   └── Wrong TTP mapping (hunting for what the attacker didn't do)

├── Data source failure
│   ├── Log source not available
│   ├── Log source sampled / low fidelity
│   ├── Insufficient retention (time window exceeds data age)
│   └── Field not populated (e.g., command line logging disabled)

└── Analysis failure
    ├── Threshold overconfidence (treating 0.31 as definitively benign)
    ├── Scope creep (hypothesis drift)
    ├── Confirmation bias (looking for evidence to support, not refute)
    └── False negative acceptance (null result without coverage statement)

Failure Mode 1: Too Broad

Hypothesis (too broad):
"Are there any PowerShell commands in our environment that could be malicious?"

Results: 847,000 events in 30 days
Analyst can triage: ~200 events/day
Time to clear: ~4,200 analyst-days

Problem: This is not a hypothesis — it's a data dump with an implied question.

Fix — Progressive narrowing:

Round 1 (scope reduction):
"PowerShell spawned by Office applications (Word, Excel, Outlook) in past 30 days"
→ Results: 3 events
→ Triageable in < 1 hour

Round 2 (if Round 1 is clean, widen hypothesis):  
"PowerShell spawned by any user application not in approved parent list"
→ Results: 47 events
→ Triageable in 4 hours

Round 3 (statistical filter):
"PowerShell commands > 500 characters OR containing -enc flag"
→ Apply across all parent processes
→ Results: 23 events
→ Triageable in 2 hours

Narrowest specific hypothesis first. Widen only if clean.


Failure Mode 2: Too Narrow / Wrong Variation

Hypothesis:
"Alert on PowerShell with -encodedcommand flag (exact string match)"

Attacker command: powershell -enc [base64]

Hunt misses: powershell /encodedcommand [base64]
             powershell -e [base64]
             powershell -EnC [base64]  (case insensitive)
             powershell.exe -ENCODEDCOMMAND [base64]

PowerShell accepts abbreviated flag forms (-e, -en, -enc, -encod…) and the parameter names are case-insensitive. An exact string match hypothesis misses 80% of real usage.

Fix: Use regex matching for known abbreviations:

| where CommandLine matches regex @"(?i)-e(n(c(o(d(e(d(c(o(m(m(a(n(d)?)?)?)?)?)?)?)?)?)?)?)? "
// Or simpler: check for the base64 pattern (long alphanum string)
| where CommandLine matches regex @"-[eE]\S* [A-Za-z0-9+/=]{50,}"

Failure Mode 3: Wrong Data Source

Example scenario: Hunt for certutil download execution.

Hypothesis: "certutil.exe with -urlcache flag appears in Windows Security Event Log"

Hunt result: 0 events

Actual state: certutil.exe ran 14 times in 30 days
Problem: Windows Security Event Log 4688 (process creation) requires audit 
         policy "Process Creation" with command line logging enabled.
         This environment has process creation auditing enabled but command 
         line capture disabled → Event 4688 shows certutil.exe ran but shows
         CommandLine: "(null)" — the -urlcache flag is invisible.

Data source validation before hunting:

Before hypothesis:
1. Check: Is Event 4688 present with non-null CommandLine? 
   → query: SecurityEvent | where EventID == 4688 | where isnotempty(CommandLine) | take 1
   → If 0 results: command line logging not enabled → switch to Sysmon Event 1

2. Check: Is Sysmon Event 1 collected in SIEM?
   → query: Sysmon | where EventID == 1 | take 1
   → If results: Sysmon is the correct source for this hunt

Failure Mode 4: Premature Closure

Pattern:

Hunt: "Find beacons to external IPs with CV < 0.30"

Step 1: Rate filter → 2,847 results
Step 2: Peer cohort filter → 43 results
Step 3: CV calculation on top 5 (by conn_count) → all clean

Analyst conclusion: "No beacons found."

MISTAKE: Top 5 by conn_count were safe harbored after calculation. 
         Candidates 6–43 were never evaluated because analyst ran out of time.

Prevention — Define explicit closure criteria in the hypothesis:

Hunt is COMPLETE when:
  - All 43 candidates have been evaluated OR
  - All remaining candidates have conn_count < 100 AND are in safe harbor
Hunt is INCONCLUSIVE if:
  - Time limit reached with candidates 6–43 unevaluated
  - Document as partial hunt: "candidates 1–5 evaluated, 6–43 pending"

Premature closure without documentation is the same as not hunting at all — it produces false confidence without the work to back it.