CHAPTER 05 35 MIN READ INTERMEDIATE

Hunt Execution

The analyst had a strong hypothesis and confirmed data sources. Then they opened the SIEM and started typing. Four hours later, they were chasing an RMM tool's scheduled maintenance task with no plan, no pivot trail, and nothing documented. Execution without structure is investigation, not hunting. This chapter covers the mechanics of running a hunt: planning before querying, writing effective queries, statistical methods, pivot chains, and capturing output that survives beyond the session.

KQL queries pivoting

Planning a Hunt

A hunt without a written plan is an ad hoc investigation. The two are not the same thing. Ad hoc investigations are reactive, undocumented, and unrepeatable. Hunts are structured, planned, and produce durable output regardless of whether they find a threat.

The hunt plan document has four required fields. All four must be filled before any query runs.

Field What It Contains Why It Matters
Hypothesis ABLE-formatted statement from Chapter 2 Defines what you are looking for. Without it, every result looks relevant.
Scope Systems, time window, environment (prod/dev/cloud), platforms Prevents scope creep mid-hunt. Documents what was NOT covered so gaps are explicit.
Data Sources Which logs, confirmed available, confirmed quality-checked If the data is not there or is poor quality, stop before wasting time. Document the gap instead.
Success Criteria What confirms the hypothesis, what rules it out Defines when the hunt is complete. Without this, hunts never end cleanly.

Time windows are a common planning failure. Too short misses slow attackers who operate below daily thresholds. Too long creates noise and slows queries. Start with 14 to 30 days for most hypotheses. If initial results suggest older activity, extend the window with a targeted follow-up query rather than re-running everything from scratch.

Tip: Write the plan before touching the SIEM. A hunter who skips the plan spends 4 hours looking at the wrong data. The plan takes 15 minutes. The wasted SIEM session costs 4 hours.
Example: Filled hunt plan for a lateral movement hypothesis.

Hypothesis: A financially-motivated actor has used PsExec (T1021.002) to move laterally from compromised workstations to servers in the production environment, leaving Service Control Manager event logs and network connection artifacts on source hosts.

Scope: All Windows endpoints in production (approx. 3,200 hosts). Cloud and development environments excluded. Time window: last 21 days. Focus on workstation-to-server connection pairs. Out of scope: Linux hosts, cloud workloads, known IT admin machines using PsExec for legitimate tasks (documented list maintained by IT ops).

Data Sources: Windows Security Event 7045 (service install, DCs and servers), Sysmon Event 3 (network connections, all endpoints), DeviceNetworkEvents (MDE). All three confirmed present and quality-checked on 2026-06-01.

Success Criteria: Hunt confirms if: Event 7045 shows PSEXESVC or a random-named service installed within 60 seconds of a Sysmon Event 3 network connection on the same host pair. Hunt is complete when all anomalous service installs are triaged, or when data quality is confirmed sufficient and no evidence is found.

Query Writing for Hunters

Hunting queries differ from detection rules. Detection rules are narrow and tuned to minimize false positives in automated alerting. Hunt queries start broad, find anomalies, and get refined through iteration. Writing a narrow query on an unvalidated hypothesis is the single most common query mistake hunters make. Zero results could mean "no threat" or "my query was too specific." You cannot tell the difference without starting broad.

KQL: Kusto Query Language

KQL powers Microsoft Sentinel and Microsoft Defender for Endpoint. The key operators for hunting: where, summarize, extend, project, join, make_set, dcount, and bin. Learn these eight and you can write 90% of the hunting queries you will ever need.

KQL
// Hunt: unexpected parent processes spawning shells (T1059 + T1036)
// Most shells should spawn from explorer.exe, terminal emulators, or dev tools.
// Unusual parents indicate LOLBin abuse, macro execution, or process injection.
DeviceProcessEvents
| where FileName in~ ("cmd.exe", "powershell.exe", "wscript.exe", "mshta.exe")
| where InitiatingProcessFileName !in~ (
    "explorer.exe", "cmd.exe", "powershell.exe", "bash.exe",
    "python.exe", "code.exe", "devenv.exe", "conhost.exe",
    "WindowsTerminal.exe", "wt.exe", "taskmgr.exe"
)
| summarize
    Count = count(),
    Devices = dcount(DeviceName),
    CmdLines = make_set(ProcessCommandLine, 10)
    by InitiatingProcessFileName, FileName
| where Count < 50  // rare combinations only — common ones are likely legitimate
| order by Count asc

The pattern: filter to the target process, exclude known-good parents, summarize to identify rare parent-child combinations, then sort ascending so the rarest results appear first. Always investigate the bottom of the list before the top.

SPL: Splunk Processing Language

SPL's key hunting commands: stats, eval, rex, transaction, lookup, and join. SPL's stats is the functional equivalent of KQL's summarize. The following query detects DNS beaconing by measuring query regularity. C2 check-ins are regular. Human browsing is not.

SPL
index=dns sourcetype=dns
| eval hour=strftime(_time, "%Y-%m-%d %H")
| stats count by src_ip, query, hour
| stats
    avg(count) as avg_hourly,
    stdev(count) as stdev_hourly,
    dc(hour) as active_hours,
    max(count) as peak_hourly
    by src_ip, query
| where active_hours > 12 AND stdev_hourly < 3 AND avg_hourly > 2
| eval beacon_score = round((avg_hourly / (stdev_hourly + 0.1)), 2)
| where beacon_score > 10
| sort -beacon_score

A high beacon score means high regularity: the host queries this domain at a consistent rate across many hours. Low standard deviation and sustained activity across more than 12 hours is the statistical signature of automated check-ins. Investigate the top-scoring pairs first.

Sigma: Vendor-Neutral Detection Format

Sigma is a detection rule format that converts to KQL, SPL, or any target SIEM via sigma-cli. It is not a query execution language. For hunters, Sigma is the documentation and sharing format. Write and validate in your SIEM's native language first. Convert to Sigma after validation for sharing and operationalizing as a detection rule in TRACERULES.

YAML (Sigma)
title: Suspicious Scheduled Task Creation via Schtasks
id: a7e3-hunt-schtask-01
status: experimental
description: Detects schtasks.exe creating tasks pointing to temp or user-writable paths
author: H3AD-LEARN Hunt Exercise
date: 2026/06/02
logsource:
  category: process_creation
  product: windows
detection:
  selection:
    Image|endswith: '\schtasks.exe'
    CommandLine|contains: '/create'
  filter_legit_paths:
    CommandLine|contains:
      - '\Windows\System32\'
      - '\Program Files\'
  condition: selection and not filter_legit_paths
falsepositives:
  - Legitimate software installers using temp paths
level: medium
tags:
  - attack.persistence
  - attack.t1053.005
Note: Write your hunt query in the language of your SIEM. Convert to Sigma after you validate it. Sigma is for sharing and detection operationalization, not for exploration. Sigma has no interactive query execution environment.

Statistical Hunting Methods

Not every hunt starts with a targeted hypothesis. Some of the most impactful findings come from applying statistical methods to baseline data and letting the anomalies surface themselves. Three core approaches are used by mature hunt teams.

Frequency Analysis and Long-Tail

Count occurrences and find outliers at the extremes. What processes run on only 1 to 2 hosts out of 5,000? Rare equals suspicious. Legitimate enterprise software runs on hundreds or thousands of hosts. A novel implant runs on 1 to 5. Sort ascending and start at the bottom.

KQL
// Long-tail: find binary hashes seen on very few devices
// Legitimate enterprise software has broad deployment footprints.
// A custom implant or staged dropper appears on only the targeted hosts.
DeviceProcessEvents
| where isnotempty(SHA256)
| summarize
    DeviceCount = dcount(DeviceName),
    ExecutionCount = count(),
    ProcessNames = make_set(FileName, 5),
    FirstSeen = min(Timestamp),
    LastSeen = max(Timestamp)
    by SHA256
| where DeviceCount between (1 .. 3)  // seen on 1-3 devices only
| where ExecutionCount < 10
| order by DeviceCount asc, ExecutionCount asc
| project SHA256, DeviceCount, ExecutionCount, ProcessNames, FirstSeen, LastSeen

Baseline Deviation

Compare the current window against a prior window. What is new that was not present last month? Attackers establishing persistence create artifacts that appear in the recent window but have no historical baseline. Any scheduled task, service, or registry run key with zero historical occurrence deserves investigation.

KQL
// Baseline deviation: new scheduled tasks this week vs. prior 30 days
// A task with no historical baseline is either newly installed software
// or an attacker establishing persistence. Investigate all results.
DeviceProcessEvents
| where FileName =~ "schtasks.exe"
| where ProcessCommandLine has "/create"
| extend TaskName = extract(@'\/tn\s+"?([^"\/\s]+)', 1, ProcessCommandLine)
| where isnotempty(TaskName)
| summarize
    RecentCount = countif(Timestamp > ago(7d)),
    HistoricalCount = countif(Timestamp between (ago(37d) .. ago(7d)))
    by TaskName
| where RecentCount > 0 and HistoricalCount == 0
| project TaskName, RecentCount, HistoricalCount

Clustering and Behavioral Outliers

Group entities by behavioral similarity. Every legitimate svchost.exe in your environment should behave similarly: same parent (services.exe), similar command-line patterns, expected network destinations. An svchost.exe that does not fit the cluster is anomalous. This is the foundation of the ML-assisted hunting approach covered in Chapter 8.

Example: You stack parent-child process chains across 10,000 endpoints. Most combinations appear thousands of times: explorer.exe → chrome.exe (8,400 occurrences), services.exe → svchost.exe (52,000 occurrences). One combination appears twice: winword.exe → cmd.exe → powershell.exe → net.exe. That is the hunt finding. Two occurrences means it is not noise. The parent chain through Word means a document spawned a shell. That is T1566.001 followed by T1059.001, and the net.exe execution suggests enumeration. The entire kill chain reconstructed from one frequency anomaly.

Pivot Techniques

A pivot is moving from one data point to a related data point. Pivots are how you turn one suspicious finding into the full scope of an intrusion. A single suspicious process hash tells you one binary ran on one host. A chain of pivots tells you which hosts are affected, what network connections were made, which accounts were used, and where lateral movement occurred.

Common pivot chains:

  • Process hash to all hosts running that hash, to network connections from those hosts, to destination IPs, to other processes connecting to the same IPs.
  • Suspicious scheduled task name to creation timestamp, to the process that created it, to the user account, to logon history for that account, to source IPs, to other accounts from the same source IP.
  • C2 domain to IP resolution, to other domains resolving to the same IP, to DNS query history, to all hosts querying those domains, to lateral movement indicators from those hosts.
Starting Point Pivot Dimension What It Reveals
Process hash All hosts running the hash Scope of compromise, deployment breadth
Affected host set Outbound network connections C2 infrastructure, exfiltration destinations
Destination IP Other domains resolving to same IP Full C2 cluster, shared infrastructure across campaigns
User account Logon history across all hosts Lateral movement path, compromised account scope
Logon source IP Other accounts authenticating from same IP Credential dumping breadth, pass-the-hash victims
Scheduled task name Process that created it Initial execution chain, staging mechanism
DNS query (suspicious domain) All hosts making the same query Full set of hosts beaconing to C2
C2 beacon process Child processes spawned from it Post-exploitation activity, lateral movement tools
KQL
// Two-step pivot: suspicious hash to network connections from affected hosts
// Replace the hash value with the actual SHA256 from your finding.
// Step 1 identifies affected hosts. Step 2 surfaces their external communications.

let SuspiciousHash = "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2";
let AffectedDevices = DeviceProcessEvents
    | where SHA256 =~ SuspiciousHash
    | distinct DeviceName;

DeviceNetworkEvents
| where DeviceName in (AffectedDevices)
| where RemoteIPType != "Private"
| where ActionType == "ConnectionSuccess"
| summarize
    ConnectionCount = count(),
    Ports = make_set(RemotePort),
    FirstSeen = min(Timestamp),
    LastSeen = max(Timestamp)
    by DeviceName, RemoteIP, RemoteUrl
| order by ConnectionCount desc

H3AD-SEC's PIVEX tool at h3ad-sec.github.io/PIVEX/ visualizes these pivot chains as interactive node graphs. When you need to scope an intrusion under time pressure, a visual pivot diagram is significantly faster than re-reading stacked query outputs.

Tip: Document every pivot as you make it. When you find the threat, you need to prove scope to the IR team. A pivot trail is your evidence chain. An undocumented pivot you cannot reproduce is not admissible as evidence of scope in an IR report.

SIEM vs. EDR Hunting

Both are valid hunting platforms. They answer different questions. Treating them as interchangeable or choosing one and ignoring the other leaves critical blind spots in any hunt.

SIEM strengths: breadth across all log sources in a single query interface, correlation between endpoint, identity, and network data, longer retention for historical analysis, full visibility into authentication and cloud activity. SIEM weaknesses: normalized data loses field fidelity, process tree context is absent or reconstructed, memory artifacts are not accessible.

EDR strengths: deep endpoint telemetry with rich raw field coverage, full process tree visualization with parent-child-grandchild chains, memory artifacts and behavioral analytics, rich process lineage data. EDR weaknesses: endpoint-only view with no identity or network context outside the host, shorter default retention in most deployments.

The practical workflow: start the hypothesis in SIEM for breadth across all hosts. When you find an anomaly, pivot to EDR for depth on that specific host and process chain. Neither platform alone is sufficient for a complete hunt.

Capability SIEM EDR
Cross-source correlation (endpoint + identity + network) Strong Weak
Full process tree depth Reconstructed, limited Full chain, native
Memory artifact visibility (injection, hollowing) Not available Full
Identity and authentication context Full (AD, cloud, VPN) Endpoint session only
Typical log retention 90 days to 1 year 30 to 90 days
Cloud activity visibility Full (with connectors) Not applicable
Behavioral analytics (on-agent) Rule-based, higher latency On-agent, lower latency
Network traffic content and proxy logs Full with proxy/firewall connectors Connection metadata only
KQL
// Hunt: CreateRemoteThread cross-process injection (T1055.001)
// This query requires EDR-level telemetry (MDE DeviceEvents).
// SIEM-normalized logs do not contain CreateRemoteThreadApiCall data.
DeviceEvents
| where ActionType == "CreateRemoteThreadApiCall"
| where InitiatingProcessFileName != FileName  // injecting into a different process
| where FileName !in~ (
    "svchost.exe", "dwm.exe", "csrss.exe",
    "SearchIndexer.exe", "WmiPrvSE.exe"
)
| summarize
    InjectionCount = count(),
    TargetProcesses = make_set(FileName, 10),
    SourceProcesses = make_set(InitiatingProcessFileName, 10)
    by DeviceName, InitiatingProcessSHA256, bin(Timestamp, 1h)
| where InjectionCount > 2
| order by InjectionCount desc
Warning: SIEM hunting on normalized logs misses context that EDR preserves. If your SIEM shows a suspicious process event, always verify the full process tree in EDR before drawing conclusions. A normalized Event 4688 tells you a process was created. The EDR process tree tells you the full lineage, the file hash, the parent command line, and every child process spawned afterward.

Iterating on Hunt Findings

A hunt rarely ends at the first query. Iteration is the mechanism for going from "something seems off" to "here is the full scope with evidence." Every initial query is a starting point, not a final answer. The results tell you what to filter, what to follow up on, and what to pivot toward.

The iteration loop runs until the hypothesis is answered:

  1. Run the initial query on the hypothesis scope.
  2. Review each result: true positive (confirmed suspicious after investigation), false positive (confirmed legitimate after investigation), or coverage gap (expected results and found none).
  3. Refine: add filters to remove false positives, extend scope to cover gaps, run follow-up pivot queries on true positives.
  4. Repeat until the hypothesis is confirmed, refuted, or a coverage gap is identified that prevents answering it.

Know when to stop. The hunt is complete when the hypothesis is answered, or when you have identified a visibility gap that prevents answering it. Both are valid outcomes. Continuing past this point without a new hypothesis is scope creep, not thoroughness.

Escalation decision tree: confirmed threat means hand to IR immediately with full documentation. Confirmed coverage gap means document and submit to the data source team as a control deficiency. New hypothesis generated during execution means add it to the hunt backlog with context from the current hunt, not as a branch of the active session.

Example: Initial query finds 3 results matching "powershell.exe with -enc flag launched from an unusual parent." You investigate each one.

Result 1: Parent is a known RMM tool. The vendor uses encoded PowerShell commands for scripted deployments. Confirmed false positive. Add the RMM parent process name to the exclusion filter.

Result 2: Parent is a software update service for a line-of-business application. Confirmed false positive after verifying with the application owner. Document with context.

Result 3: Parent is cmd.exe, grandparent is winword.exe. The Word process opened a file from a path matching an email attachment staging directory two minutes prior. True positive: phishing-delivered macro executing encoded PowerShell. Escalate to IR. Generate a new backlog hypothesis: "Hunt for other hosts where winword.exe spawned command-line processes in the same 72-hour window."

Capturing Hunt Output

A hunt with no documented output is a wasted hunt. You cannot reproduce it, share it, build a detection from it, or use it to measure program effectiveness. Documentation is not optional overhead. It is the mechanism by which a hunt session creates lasting value beyond the analyst who ran it.

Minimum viable output record for every hunt:

  1. Hypothesis in ABLE format.
  2. All queries run, in copy-paste-ready format with any variable substitutions noted inline.
  3. Data sources used, with quality status confirmed or noted as degraded.
  4. Findings summary: what you found and what you explicitly did not find.
  5. Result classification: TP, FP, Coverage Gap, No-Find, or Escalated.
  6. New detections created, with links to TRACERULES at h3ad-sec.github.io/TRACERULES/.
  7. New hypotheses generated during execution, ready for the backlog with enough context to reprioritize them.

The full lifecycle documentation process is covered in Chapter 7. What matters here: capture output during execution, not after. A running execution log written as you work is far more accurate than a report written from memory an hour after the session ends. Tools for documentation include OTRF-style Jupyter notebooks, an internal wiki, or H3AD-SEC's QUERYBASE at h3ad-sec.github.io/QUERYBASE/ for storing validated hunt queries.

Tip: Null findings have real value. A hunt that confirms no evidence of T1003 in your environment, with confirmed good data quality, means your credential access detection coverage is working. Document it explicitly as a No-Find with good data. In six months, when someone asks whether you have hunted for credential dumping recently, you have a specific, dated, documented answer.

Key Takeaways

  • A hunt plan requires four fields before any query runs: hypothesis, scope, data sources, and success criteria. Skipping the plan turns a hunt into an ad hoc investigation.
  • KQL's dcount() counts unique values. make_set() aggregates distinct values into an array. summarize ... by groups results. These three patterns cover most hunting aggregations.
  • Long-tail analysis (sort ascending by count) surfaces novel threats hiding at low-frequency outliers. Adversaries operate below the noise threshold of most detection rules.
  • Pivots build scope. One suspicious hash tells you nothing about the intrusion. A pivot chain from that hash through network connections to accounts and lateral movement tells you the full picture.
  • SIEM provides breadth across log sources. EDR provides depth on individual hosts. A complete hunt uses both, not one or the other.
  • Sigma is a documentation and sharing format, not an execution language. Write and validate in your SIEM's native language first, then convert to Sigma for sharing and detection operationalization.
  • Every hunt must produce documented output regardless of whether it finds a threat. Null findings confirm coverage. Coverage gaps reveal control deficiencies. Both outcomes have value.

Knowledge Check

Click an answer to reveal the explanation.

Which KQL operator counts the number of unique values in a column?

dcount() returns the distinct count of values in a column. count() returns the total row count. make_set() returns an array of distinct values, not a count. distinct is a tabular operator used to deduplicate rows, not an aggregation function.

In long-tail analysis, why do you sort results by count ascending rather than descending?

Adversaries operate at low frequencies to avoid triggering detection thresholds. Legitimate enterprise software runs on thousands of hosts. A custom implant or staged dropper runs on 1 to 5. Sorting ascending puts the rarest events at the top of your results, exactly where novel threats live. The high-frequency entries at the bottom are almost always legitimate.

What is the primary purpose of Sigma rules in a threat hunting workflow?

Sigma is a vendor-neutral detection rule format designed for sharing and operationalizing validated detections. It has no native execution environment. Write and validate in your SIEM's native language first (KQL, SPL, etc.), then convert to Sigma for sharing with peers and submitting to a detection rule library like TRACERULES.

When should a hunt finding be escalated to the IR team?

Escalate when you have a confirmed true positive: activity that remains suspicious after ruling out all known false positive patterns. Coverage gaps go to the data source team as control deficiencies. Refuted hypotheses are documented as No-Find. Raw, uninvestigated query results are never a sufficient basis for IR escalation.

What is a pivot in threat hunting?

A pivot uses one data point (a suspicious hash, a destination IP, a user account) as the anchor for a new query that finds related data. Pivot chains turn a single suspicious indicator into a full intrusion scope map. Documenting each pivot step is how you build the evidence chain that proves scope to the IR team and to management.
VISITORS
VISITORS