Skip to content
intermediate Phase 14 · SOC Operations and SIEM Platforms

Splunk SPL Fundamentals

Write searches that aggregate, correlate, and visualize security events.

1h
0 problems
Topic Progress 0%

SPL Syntax: The Building Blocks

Search Processing Language: How Splunk Queries Work

Splunk's Search Processing Language (SPL) is a pipe-based query language. You start with a base search that retrieves events, then pipe the results through transforming commands that filter, aggregate, and visualize. Each command in the pipe receives the output of the previous command as its input.

index=windows EventCode=4625 | stats count by src_ip | sort -count

This query: (1) retrieves all Windows failed logon events, (2) counts them grouped by source IP, (3) sorts by count descending. The pipe character (|) chains commands together.

The Base Search

The base search is everything before the first pipe. It specifies which events to retrieve using index, source, sourcetype, and field filters.

# Search the Windows security index for failed logons
index=windows EventCode=4625

# Search for events from a specific source host
index=windows host=WS-042 EventCode=4625

# Search using wildcards
index=windows EventCode=462* EventCode=468*

# Search across multiple indexes
index=windows OR index=linux sourcetype=auth

Time Range

Every search has a time range. In the Splunk UI, this is the time picker. In SPL, you can use earliest and latest modifiers:

# Search the last 15 minutes
index=windows EventCode=4625 earliest=-15m

# Search a specific time range
index=windows EventCode=4625 earliest="09/30/2026:00:00:00" latest="09/30/2026:23:59:59"

Indexed Fields vs Extracted Fields

Indexed fields are extracted at index time — when Splunk ingests the event. They are fast to search because Splunk has already built an index of their values. Common indexed fields: host, source, sourcetype, index. You can also configure custom indexed fields in props.conf and transforms.conf.

Extracted fields are extracted at search time — when you run a query. They are slower because Splunk must evaluate the extraction against every event in the search results. Field extractions defined in props.conf (using regex or delimiters) are search-time extractions.

# Indexed field search (fast)
index=windows host=WS-042

# Extracted field search (slower — evaluated at search time)
index=windows src_ip=192.168.1.100

The performance difference matters at scale. Searching an indexed field on 100 million events is fast because Splunk uses the index to jump directly to matching events. Searching an extracted field on the same volume is slower because Splunk must process every event. For frequently searched fields, consider making them indexed fields.

Filtering with search and where

The search command filters events based on field values. It is used after the base search to further refine results.

# Filter for events where the action is "failure"
index=windows EventCode=4625 | search action="failure"

# Filter using wildcards
index=windows | search user="admin*"

# Filter using Boolean logic
index=windows EventCode=4625 | search src_ip=192.168.* AND user="admin"

The where command is similar but operates on the results of a transforming command (like stats). It filters aggregated results rather than raw events.

# Show only source IPs with more than 10 failed logons
index=windows EventCode=4625 | stats count by src_ip | where count > 10

Transforming Commands: Aggregation and Visualization

From Raw Events to Actionable Intelligence

Retrieving events is only the beginning. Transforming commands aggregate, calculate, and reshape the data into formats that answer security questions. The most important transforming commands for SOC work are stats, eval, timechart, and transaction.

stats: The Workhorse

stats aggregates events into summary tables. It is the most commonly used command in SOC searches.

# Count events by source IP
index=windows EventCode=4625 | stats count by src_ip

# Count with a custom column name
index=windows EventCode=4625 | stats count as failed_logons by src_ip

# Multiple aggregations
index=windows EventCode=4625 | stats count as attempts, dc(src_ip) as unique_sources, values(user) as targeted_users by src_ip

# Calculate statistics
index=windows EventCode=4625 | stats count, min(_time) as first_attempt, max(_time) as last_attempt by src_ip

Key stats functions:

  • count — number of events
  • dc() — distinct count (number of unique values)
  • values() — list of all values
  • sum(), avg(), min(), max() — mathematical aggregations
  • list() — list of values (including duplicates)

eval: Calculated Fields

eval creates new fields or modifies existing ones using expressions. It is used for calculations, conditional logic, and data transformation.

# Create a severity field based on count
index=windows EventCode=4625 | stats count by src_ip | eval severity=if(count>100, "critical", if(count>10, "high", "medium"))

# Extract a substring
index=windows EventCode=4688 | eval command_length=len(command_line)

# Convert epoch time to human-readable
index=windows | eval readable_time=strftime(_time, "%Y-%m-%d %H:%M:%S")

# Conditional logic with case
index=windows EventCode=4625 | eval auth_type=case(Logon_Type==2, "interactive", Logon_Type==3, "network", Logon_Type==10, "remote_interactive", true(), "other")

timechart: Temporal Analysis

timechart creates time-series visualizations. It buckets events into time intervals and aggregates within each bucket.

# Failed logons per hour over the last 24 hours
index=windows EventCode=4625 | timechart span=1h count by src_ip

# Multiple metrics over time
index=windows EventCode=4625 | timechart span=1h count as failed, dc(src_ip) as unique_sources

# Detect spikes — compare current count to average
index=windows EventCode=4625 | timechart span=1h count | eventstats avg(count) as baseline | eval deviation=count-baseline | where deviation > 3*baseline

The span parameter controls the bucket size. For security monitoring, common spans are 1m (real-time), 5m (near-real-time), 1h (hourly trends), and 1d (daily trends).

transaction: Session Reconstruction

transaction groups related events into sessions based on a common field. It is used to reconstruct user sessions, connection flows, and multi-step processes.

# Group events by user session
index=windows EventCode=4624 OR EventCode=4634 | transaction user maxspan=8h

# Calculate session duration
index=windows EventCode=4624 OR EventCode=4634 | transaction user maxspan=8h | eval duration=duration/60 | stats avg(duration) by user

transaction is powerful but expensive — it must hold events in memory until the session closes. Use it judiciously on large datasets.

lookup: Enrichment

lookup joins search results with external data — asset inventories, threat intelligence feeds, user directories.

# Enrich with asset inventory
index=windows EventCode=4625 | stats count by src_ip | lookup asset_inventory ip as src_ip OUTPUT hostname, owner, criticality

# Enrich with threat intelligence
index=windows EventCode=4625 | stats count by src_ip | lookup threat_intel ip as src_ip OUTPUT threat_category, confidence

Lookups transform raw IP addresses into actionable context — instead of "192.168.1.100 has 500 failed logons," you see "WS-042 (Finance server, owner: J. Smith, criticality: high) has 500 failed logons."

Correlation, Subsearches, and Alerting

Connecting the Dots Across Data Sources

The most powerful SPL queries correlate events across multiple data sources to detect complex attack patterns. A single alert may be noise; the same alert correlated with a suspicious process creation and a network connection to a known C2 server is a confirmed incident.

Subsearches

A subsearch is a search inside a search. The inner search runs first, and its results are used as input to the outer search. Subsearches are enclosed in square brackets.

# Find failed logons from IPs that also appear in the firewall deny log
index=windows EventCode=4625 [search index=firewall action=deny | fields src_ip | dedup src_ip] | stats count by src_ip, user

The subsearch [search index=firewall action=deny | fields src_ip | dedup src_ip] retrieves all source IPs that were denied by the firewall. The outer search then finds failed logon events from those same IPs. This correlates two data sources — Windows authentication logs and firewall logs — in a single query.

Subsearches have a default limit of 10,000 results and a default timeout of 60 seconds. For large result sets, use the format command to reduce the output:

# Subsearch with format to reduce output size
index=windows EventCode=4625 [search index=firewall action=deny | fields src_ip | dedup src_ip | format] | stats count by src_ip

Correlation Patterns

Brute Force Detection

# Detect brute force: more than 20 failed logons in 5 minutes from the same source
index=windows EventCode=4625 earliest=-5m | bucket _time span=5m | stats count by _time, src_ip | where count > 20

Impossible Travel

# Detect impossible travel: same user logging in from different countries within 1 hour
index=windows EventCode=4624 Logon_Type=10 | iplocation src_ip | stats dc(Country) as countries, values(Country) as country_list by user | where countries > 1

Malware Execution Chain

# Detect PowerShell downloading a payload and then executing it
index=windows EventCode=4104 [search index=windows EventCode=4688 | search command_line="*powershell*" | fields _time, host] | stats count by host

Scheduled Searches: From Hunt to Alert

A manual hunt becomes a detection when you schedule it to run automatically and generate alerts. In Splunk, this is a saved search with an alert action.

# Scheduled search: brute force detection every 5 minutes
index=windows EventCode=4625 earliest=-5m | bucket _time span=5m | stats count by _time, src_ip | where count > 20

To convert this to an alert:

  1. Save the search with a descriptive name: "Brute Force Detection - Windows Auth"
  2. Set the schedule: every 5 minutes
  3. Set the alert condition: when number of results > 0
  4. Set the alert action: create a case in the case management system, send an email to the SOC, or trigger a webhook

The alert should include context in the notification: the search query, the triggering results, and a link to the search in the SIEM. The analyst receiving the alert should have enough information to begin triage without opening the SIEM.

Alert Tuning

A scheduled search that generates too many false positives is worse than no alert — it causes alert fatigue. Tuning techniques:

  1. Add thresholds — where count > 20 instead of where count > 1
  2. Add context filters — exclude known-good sources: where NOT src_ip IN (10.0.0.10, 10.0.0.11)
  3. Use baseline comparison — alert only when the count exceeds the historical average by a significant margin
  4. Suppress known patterns — use suppress rules for scheduled maintenance windows or penetration tests
  5. Test before deploying — run the search over historical data to see how many alerts it would have generated
# Tuned alert: brute force with threshold and known-good exclusion
index=windows EventCode=4625 earliest=-5m src_ip!=10.0.0.10 src_ip!=10.0.0.11 | bucket _time span=5m | stats count by _time, src_ip | where count > 20

SPL Best Practices

  1. Filter early — apply filters in the base search before any transforming commands. This reduces the data volume that subsequent commands must process.
  2. Use indexed fields — search on indexed fields (host, source, sourcetype) whenever possible for better performance.
  3. Limit time ranges — search the narrowest time range that answers the question. Searching "all time" is slow and expensive.
  4. Use fields to reduce output — after filtering, use fields to keep only the columns you need. This reduces memory usage and speeds up the search.
  5. Document your searches — add comments explaining what the search does, why it exists, and what the expected results are.

Practice Problems

0 / 2 solved
Write a Brute Force Detection Search

Solution
```
index=linux sourcetype=auth message="Failed password" earliest=-5m
| bucket _time span=5m
| stats count as attempts, values(user) as targeted_users by _time, src_ip
| where attempts > 10
| sort -attempts
```
This query: (1) retrieves failed SSH authentication events from the last 5 minutes, (2) buckets them into 5-minute windows, (3) counts attempts and collects targeted usernames per source IP per window, (4) filters for IPs with more than 10 attempts, (5) sorts by attempt count descending. The `bucket` command creates the time windows, and `stats` aggregates within each window.
Correlate Firewall and Authentication Logs

Solution
```
index=windows EventCode=4624 Logon_Type=3 earliest=-1h
[search index=firewall action=deny earliest=-1h | fields src_ip | dedup src_ip | format]
| stats count as successful_logons, values(user) as users by src_ip
| sort -successful_logons
```
The subsearch retrieves all source IPs denied by the firewall in the last hour. The outer search finds successful network logons (Logon_Type=3) from those same IPs. This correlation detects the pattern where an attacker's initial connection attempts were blocked by the firewall but a subsequent attempt succeeded — indicating a potential breach.

Quiz

1. What is the difference between indexed fields and extracted fields in Splunk?

Question 1 options

2. What does the `stats` command do in SPL?

Question 2 options

3. What is a subsearch in SPL and how does it work?

Question 3 options

4. Why is filtering early in an SPL query important for performance?

Question 4 options

5. What is the purpose of the `timechart` command?

Question 5 options

Flashcards

Question

What is SPL?

Answer

Splunk Search Processing Language — a pipe-based query language where a base search retrieves events and transforming commands (stats, eval, timechart) filter, aggregate, and visualize the results.

Question

What is the difference between indexed and extracted fields?

Answer

Indexed fields are extracted at index time and are fast to search (Splunk uses the index to jump to matching events). Extracted fields are evaluated at search time and are slower (Splunk must process every event).

Question

What does the `stats` command do?

Answer

Aggregates events into summary tables. Key functions: count, dc (distinct count), values, sum, avg, min, max. The most commonly used transforming command in SOC searches.

Question

What is a subsearch?

Answer

A search enclosed in square brackets that runs before the outer search. Its results are used as input to the outer search, enabling correlation across data sources. Default limit: 10,000 results, 60-second timeout.

Question

What does `timechart` do?

Answer

Creates time-series visualizations by bucketing events into time intervals (span parameter) and aggregating within each bucket. Essential for detecting trends and spikes in security data.

Question

How do you convert a manual hunt into an alert in Splunk?

Answer

Save the search with a descriptive name, set a schedule (e.g., every 5 minutes), set an alert condition (e.g., when number of results > 0), and configure an alert action (create case, send email, trigger webhook).

Revision Notes

Key Takeaways

  • 1. SPL is pipe-based: base search retrieves events, transforming commands aggregate and visualize
  • 2. Indexed fields are fast (index-time extraction); extracted fields are slower (search-time evaluation)
  • 3. stats is the workhorse command — count, dc, values, sum, avg for aggregation
  • 4. Subsearches (square brackets) enable correlation across data sources
  • 5. timechart creates time-series visualizations with configurable span
  • 6. Convert hunts to alerts by saving with a schedule, condition, and action

Interview Tips

  • • Walk through a concrete SPL query you have written, explaining each pipe stage
  • • Explain the performance difference between indexed and extracted fields with an example
  • • Describe how you would detect a brute force attack using SPL — bucket, stats, where
  • • Discuss subsearch use cases — correlating firewall denies with authentication logs
  • • Explain how you tune a scheduled search to reduce false positives (thresholds, exclusions, baselines)
  • • Mention SPL best practices: filter early, use indexed fields, limit time ranges, document queries

Cheat Sheet

Splunk SPL Fundamentals Cheat Sheet

Phase: SOC Operations and SIEM Platforms | Level: intermediate

SPL structure:

base_search | transforming_command | transforming_command
index=windows EventCode=4625 | stats count by src_ip | sort -count

Key commands:

  • search — filter events by field values
  • where — filter aggregated results
  • stats — aggregate (count, dc, values, sum, avg, min, max)
  • eval — calculated fields (if, case, len, strftime)
  • timechart — time-series (span=1h, span=5m)
  • transaction — session grouping (maxspan=8h)
  • lookup — join with external data
  • fields — select columns to keep
  • dedup — remove duplicates
  • sort — order results

Indexed vs extracted fields:

  • Indexed: extracted at index time, fast search (host, source, sourcetype)
  • Extracted: evaluated at search time, slower (custom fields)

Subsearches:

index=windows EventCode=4624 [search index=firewall action=deny | fields src_ip | dedup src_ip | format]

Inner search runs first, results feed outer search. Limit: 10,000 results, 60s timeout.

Alert creation:

  1. Save search with descriptive name
  2. Set schedule (e.g., every 5 minutes)
  3. Set alert condition (results > 0)
  4. Set alert action (case, email, webhook)

Best practices:

  • Filter early in the base search
  • Use indexed fields when possible
  • Limit time ranges
  • Use fields to reduce output
  • Document searches with comments

Tags: splunk, siem

Continue Learning

Before You Start

  • No prerequisites listed.