Tools · SIEM and detection engineering · 02 / 24
Splunk stats, timechart, and eval: turning raw events into detections
Use stats with dc, values, earliest, and latest, create fields with eval, chart activity over time, and combine them into a simple threshold detection.
stats functions you will use daily
count counts events. dc(field) counts distinct values. values(field) lists unique values. earliest(_time) and latest(_time) give the first and last time seen. sum, avg, and max work on numbers. Grouping with by creates one row per combination.
index=proxy earliest=-1d
| stats count dc(dest_host) as hosts values(category) as categories
earliest(_time) as first latest(_time) as last by user
| eval first=strftime(first, "%F %T"), last=strftime(last, "%F %T")
| sort - hostseval and where
eval creates or changes fields with functions such as if, case, len, lower, round, and strftime. where filters with expressions after a transform, while search filters with keyword-style matching. A common detection pattern computes a ratio or a flag with eval and keeps rows with where.
index=wineventlog EventCode IN (4624, 4625) earliest=-1h | eval outcome=if(EventCode=4624, "success", "failure") | stats count(eval(outcome="failure")) as fails count(eval(outcome="success")) as ok by src_ip | where fails > 30 AND ok > 0
Lab: chart and threshold
Using a sample dataset with authentication or web logs, chart events over time per status and spot spikes. Then build a search that flags any source with more than 30 failures and at least one success in an hour, which is a password spraying or guessing pattern followed by a hit. Save it as an alert in your lab instance with a 1-hour schedule.
index=botsv1 sourcetype=stream:http earliest=0 | timechart span=10m count by status limit=6 index=web earliest=-7d | bin _time span=1h | stats count by _time, src_ip | eventstats avg(count) as avg stdev(count) as sd by src_ip | where count > avg + 3*sd
From thresholds to baselines
Fixed thresholds are easy but brittle. eventstats and streamstats compute averages and deviations alongside each row, which lets you flag what is unusual for that source rather than using one number for everyone. Start simple, measure false positives, and add baselines where the noise is too high.
Diagram
stats functions at a glance
Pick the function that answers your question.
- count
How many events?
- dc(field)
How many distinct users, hosts, or IPs?
- values(field)
Which distinct values appeared?
- earliest / latest
When did this start and stop?
- avg / stdev with eventstats
Is this row unusual compared to the baseline?
Check yourself
1. Which stats function counts distinct values of a field?
2. After stats, which command filters rows with an expression such as fails > 30?
3. Which command adds aggregate values such as an average to every row without collapsing them?