Most Splunk hunts are slow, unrepeatable raw-event searches that one analyst wrote once and nobody can rerun with confidence. Search Processing Language (SPL) is the query language of Splunk Enterprise and Splunk Cloud Platform, and used well it turns a hunting hypothesis into a fast, documented search that can later become a detection. This guide covers the SPL commands and patterns that matter for hunting: tstats against Common Information Model (CIM) data models, stats, eventstats and streamstats for baselines and sequences, and worked hunts mapped to MITRE ATT&CK. It is for threat hunters, SOC analysts and detection engineers. Platform and app features such as risk-based alerting are covered in the Splunk Enterprise Security Guide; this page stays on the language.
Key takeaways
- Use tstats first: it reads indexed fields in tsidx files from indexed data or accelerated data models, so it is much faster than
statsover raw events for wide time ranges. - Normalise with CIM: hunting against
Endpoint.ProcessesorAuthenticationfields such asprocess_name,parent_process_name,useranddestlets one hunt run across many log sources. - Know summariesonly:
summariesonly=truereturns only summarised data, which is fast but silently misses events outside the acceleration range. - Structure hunts with PEAK: Splunk's Prepare, Execute and Act with Knowledge framework covers hypothesis, baseline and model-assisted hunts, and ends with documentation and automation.
- Hunt your machine identities: first-seen sources for service accounts and API users are among the highest-value hunts, because those accounts should behave predictably.
What the tool does
SPL is a pipeline language: a search retrieves events from indexes, then each command after a pipe (|) transforms the results. For hunting, four families of commands do most of the work.
| Command | What it does | Hunting use |
|---|---|---|
tstats | Statistical queries on indexed fields in tsidx files, from indexed data or accelerated data models | Fast population-wide counts over weeks or months |
stats | One summary row per group over all results | Counts, distinct counts, first and last seen per entity |
eventstats | Adds aggregate values to every event without collapsing them | Compare each event to its group average or total |
streamstats | Running statistics for each event as it is seen, using only earlier events by default | Time gaps between connections, sequences, bursts |
where, eval, rex | Filter on expressions, compute fields, extract with regular expressions | Command-line patterns, thresholds, derived fields |
lookup, inputlookup | Join results to CSV or KV store tables | Allow lists, asset and identity context, known-bad lists |
tstats details that change results
- summariesonly: defaults to false. When false, time ranges beyond the data model's summary range are searched from slower raw data; when true, only summarised data is returned and unaccelerated data models are not searched. Splunk's own security content wraps this in a macro (
security_content_summariesonly) so each environment can choose. - FROM datamodel=: selects a data model and root dataset, for example
datamodel=Endpoint.Processes; child datasets are reached with anodenamefilter in the WHERE clause. - BY clause: wildcards are not allowed; group
_timewithspan. Fields come back prefixed with the dataset name (Processes.process_name) until renamed. - Limits:
evalexpressions cannot go insidetstatsaggregate functions, a single search cannot cover multiple time ranges, and a filter on a high-cardinality field can be slower than a normal search, so test both.
Defender use cases
The hunts below are defensive searches against your own telemetry. Each assumes CIM-mapped data; replace index and macro names with your own, and run them first over a short window.
1. Encoded PowerShell commands (T1059.001, T1027)
Splunk's published detection "Malicious PowerShell Process - Encoded Command" uses this shape. A simplified hunting version:
| tstats summariesonly=false count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.process_name IN ("powershell.exe","pwsh.exe")
by Processes.dest Processes.user Processes.parent_process_name Processes.process
| rename Processes.* as *
| where match(process, "(?i)\s-(e|ec|enc|encodedcommand)\s")
| sort - count
Review parent processes first: encoded commands launched by Office applications, browsers or wmiprvse.exe deserve priority over those from known management tools. See the PowerShell Security Guide.
2. Rare parent and child process pairs (baseline hunt)
| tstats count from datamodel=Endpoint.Processes
by Processes.parent_process_name Processes.process_name
| rename Processes.* as *
| eventstats sum(count) as total by parent_process_name
| where count < 5 AND total > 1000
This finds child processes that a common parent almost never launches. The eventstats step keeps rare pairs only under parents with enough history to make "rare" meaningful.
3. Services installed from unusual paths (T1543.003)
index=wineventlog source="WinEventLog:System" EventCode=7045
NOT (ImagePath IN ("*:\\Windows\\*", "*:\\Program File*"))
| stats count min(_time) as firstTime values(ImagePath) as ImagePath by Computer ServiceName
Splunk's "Windows Service Created with Suspicious Service Path" detection uses Event ID 7045 with similar path exclusions and is linked to analytic stories including Qakbot and PlugX.
4. Password spraying (T1110.003)
| tstats dc(Authentication.user) as users count from datamodel=Authentication
where Authentication.action="failure" by Authentication.src _time span=15m
| rename Authentication.* as *
| where users > 20
One source failing against many distinct users in a short window is the spraying pattern. See the Password Spraying and Credential Stuffing Defence Guide.
5. First-seen sources for service accounts (T1078)
| tstats min(_time) as firstSeen from datamodel=Authentication
where Authentication.action="success" Authentication.user="svc_*"
by Authentication.user Authentication.src
| rename Authentication.* as *
| where firstSeen > relative_time(now(), "-24h")
Run it over 30 to 90 days so "first seen" is meaningful. A service account authenticating from a new host is a strong signal because its normal sources are few and fixed. Replace the svc_* pattern with a lookup of your service account inventory. See the Service Account Security Guide.
6. Regular outbound connections (T1071.001)
index=proxy
| sort 0 src dest _time
| streamstats current=f last(_time) as prev by src dest
| eval gap=_time-prev
| stats count avg(gap) as avg_gap stdev(gap) as sd_gap by src dest
| where count > 50 AND sd_gap < 5
Low variation in the gap between connections suggests automated check-ins. Expect noise from update agents and monitoring tools, and build an allow list lookup. See the Beaconing and C2 Detection Guide.
Deployment and safe use
Phased approach
- Check data readiness: confirm which sources are mapped to the CIM data models you need (Endpoint, Authentication, Network_Traffic, Web) and which models are accelerated, and over what summary range.
- Write the hypothesis: following PEAK, record the topic, the ATT&CK technique, the data needed and what a positive result looks like before writing SPL.
- Develop on a short window: run over 24 hours, check results by hand, then widen. Use
tstatsfor the wide pass and raw search to pivot on specific hosts. - Tune with lookups, not edits: keep exclusions in a lookup or a filter macro (Splunk's security content ends each search with a
..._filtermacro) so the core logic stays reviewable. - Act: document findings, open cases for anything confirmed, and promote stable hunts into scheduled detections. Splunk's security content repository stores detections as YAML and builds them with the
contentctltool, which is a useful model for version control. - Roll back: if a promoted hunt floods the queue, disable the saved search, return it to hunt status and tune before re-enabling.
Running hunts safely
- Protect search head capacity: long raw-event searches over all indexes compete with scheduled detections. Always name the index, constrain the time range and prefer
tstats. - Restrict index access by role: hunters need broad read access, but sensitive indexes (HR, legal, payment data) should be granted per role with
srchIndexesAllowedinauthorize.confand reviewed. - Handle results as evidence: exported results can contain usernames, IP addresses and command lines with secrets. Store them in the case record, not in chat or email.
- Treat hunt service accounts as NHIs: API tokens used by notebooks or automation to run SPL should be owned, scoped and rotated. See the API Key Management Guide.
Detection and hardening value
Each hunt should end in one of three outcomes: a confirmed incident, a new or improved detection, or a documented telemetry gap. Hunting is also how you find that a CIM mapping is broken or that a data model's acceleration has lagged.
Metrics
- Hunts completed per month with a recorded hypothesis and outcome.
- Hunt-to-detection conversion: percentage of hunts that produce a scheduled search.
- ATT&CK coverage change: techniques with at least one tested detection, before and after each hunting cycle.
- Telemetry gaps closed: missing sources or broken CIM mappings found and fixed.
- Median search runtime for promoted hunts, to keep scheduled load predictable.
Framework mapping
| Framework | Reference | Relevance |
|---|---|---|
| NIST SP 800-53 Rev. 5 | RA-10 Threat Hunting | Establishing and running a hunting capability |
| NIST SP 800-53 Rev. 5 | AU-6 Audit Record Review, Analysis, and Reporting | Analysing logs for indications of compromise |
| NIST CSF 2.0 | DE.CM-09 Computing hardware, software and runtime environments monitored | Endpoint process hunts |
| NIST CSF 2.0 | DE.CM-01 Networks and network services monitored | Beaconing and network hunts |
| NIST CSF 2.0 | DE.AE-02 Potentially adverse events analysed | Hunt triage and pivoting |
| MITRE ATT&CK v20 | T1059.001, T1027, T1543.003, T1110.003, T1078, T1071.001 | Worked hunts above |
Common failure patterns
- summariesonly=true over a short acceleration range: a 90-day hunt against a model accelerated for 7 days returns 7 days of data without warning.
- Unmapped sources: a host whose logs never reached the data model is invisible to every
tstatshunt; check coverage with a count by source type per data model. - Exclusions inside the search: long hard-coded NOT lists hide what the hunt really looks for and drift out of date.
- No hypothesis, no outcome: exploratory searches with no recorded result cannot be repeated or measured.
- Join and transaction overuse: these commands are memory-hungry on large data; most hunting correlations can be written with
statsby a shared field.
Alternatives
| Language | Platform | When it is the better fit |
|---|---|---|
| SPL | Splunk Enterprise and Splunk Cloud Platform | Your telemetry is in Splunk and mapped to CIM |
| KQL | Microsoft Sentinel, Defender XDR, Azure Data Explorer | Microsoft-centric estate; see the KQL for Threat Hunting Guide |
| ES|QL, EQL | Elastic Security | Elastic stack, sequence queries; see the Elastic Security Guide |
| Sigma | Vendor-neutral rule format converted to SPL, KQL and others | You share detections across platforms; see the Sigma Rules Guide |
Small teams should start from Splunk's published security content and adapt it rather than writing every hunt from scratch. Large teams should keep hunts in version control with a review step, the same way they treat detection code.
Practitioner checklist
- List which data models are accelerated and their summary ranges before hunting.
- Verify CIM coverage per source type for Endpoint, Authentication and Network_Traffic.
- Record a PEAK-style hypothesis, ATT&CK ID and expected evidence for every hunt.
- Use
tstatsfor wide passes and raw search only for pivots. - Keep exclusions in lookups or filter macros.
- Hunt first-seen sources for every service account and API user.
- Promote stable hunts to scheduled searches in version control.
- Track hunt-to-detection conversion and telemetry gaps each month.
Standards and references
- Splunk: tstats command reference
- Splunk: streamstats command reference
- Splunk: CIM Endpoint data model
- Splunk: CIM Authentication data model
- Splunk: Security Content (ESCU) repository
- Splunk SURGe: PEAK Threat Hunting Framework
- MITRE ATT&CK: PowerShell
- NIST: SP 800-53 Rev. 5
Related NHI Mgmt Group resources: Splunk Enterprise Security Guide · Threat Hunting Guide · KQL for Threat Hunting Guide · Identity Threat Hunting Guide