If you are reading this blog post via a 3rd party source it is very likely that many parts of it will not render correctly. Please view the post on signalscorps.com for the full interactive viewing experience.
In this post I will take a look at creating basic SPL searches for Splunk (and show a manual conversion of a Sigma rule to a Splunk Detection).
In the last two posts I have covered YARA-L and Kusto query languages for threat detection. In this tutorial the structure of Splunk detections for Analytic Stories are the focus.
Analytic Stories and their corresponding searches (aka detections) are built using YAML files.
In Splunk products Analytic Stories contain much more than just detections, however, for the purpose of this post, it is only the detection content I will focus on.
The detection YAML file contains lots of properties used by Splunk products to process, display and handle the event when it is triggered by the detection.
Splunk Detection YAML Structure
Before I jump into the search (aka the detection logic), lets take a look at the properties that can be used in a detection yml
file.
id
(required): UUID v4 as unique identifier- e.g. “fb4c31b0-13e8-4155-8aa5-24de4b8d6717”
name
(required): Descriptive title of the detection- e.g. “Access LSASS Memory for Dump Creation”
description
(required): A detailed description of the detection- e.g. “dbgcore.dll is a specific DLL for Windows core debugging. It is used to obtain a memory dump of a process.”
author
(required): Author of the detection- e.g. “Signals Corps”
date
(required): date of creation or modification, format yyyy-mm-dd- e.g. 2019-12-06
version
(required): version of detection- e.g. 1
datamodel
(required): datamodel(s) used by the search from list of items- e.g. Endpoint
type
(required): type of detection from list of items- e.g. Anomaly
known_false_positives
(required): description of known false positives- e.g. “Administrators can create memory dumps for debugging purposes, but memory dumps of the LSASS process would be unusual.”
references
(optional): A list of references for the detection- e.g. [“https://site1.com”,”https://site2.com”]
how_to_implement
(optional): information about how to implement. Only needed for non standard implementations- e.g. [“This search requires Sysmon Logs and a Sysmon configuration, which includes EventCode 10 for lsass.exe.”]
items
(optional): An explanation about the purpose of the detection. Unlikedescription
, could be a links to extenal sources- e.g. [“https://site1.com”,”https://site2.com”]
tags
(required): An array of key value pairs for tagging
A more detailed note on tags
The following tags are currently natively supported by other Splunk features (inc. rendering in UI)
analytic_story
: Relevant Splunk Analytic story. See Analytic Storiesmitre_attack_id
: MITRE ATT&CK Technique or Sub-technique ID. See MITRE ATT&CK.cis20
: The Center for Internet Security (CIS) Top 20 Critical Security Controls reference. See CIS.nist
: NIST Cybersecurity Framework reference. See NIST.security_domain
: Splunk Security domain. e.g networkasset_type
: Type of asset being investigated in string format. e.g AWS Instancerisk_object
: see risk scoringrisk_object_type
: see risk scoringrisk_score
: see risk scoring
Here is an example;
tags:
analytic_story:
- credential_dumping",
kill_chain_phases:
- Action on Objectives
mitre_attack_id:
- T1078.004
cis20:
- CIS 13
nist:
- DE.DP
security_domain:
- network
asset_type:
- AWS Instance
risk_object:
- user
risk_object_type:
- network_artifacts
risk_score:
- 60
In addition to these standard properties, any "custom_key": "custom_value"
pairing can be used in the tag section. For example;
tags:
product:
- Splunk Enterprise
- Splunk Enterprise Security
- Splunk Cloud
The Search
The one property I missed from the YAML structure above was the required search
.
Indeed the crucial part of the YAML file is the detection (search
) content written in Splunk’s Search Processing Language (SPL).
SPL encompasses all the search commands and their functions, arguments, and clauses. Its syntax was originally based on the Unix pipeline and SQL. The scope of SPL includes data searching, filtering, modification, manipulation, insertion, and deletion.
Like Kusto, SPL is a generic search language for logs, but has seen widespread adoption by security teams using Splunk as a SIEM.
Unlike Kusto, the Splunk data store is not based on a tabular structure. Fields are extracted at search using regular expressions, apart from fixed fields including (but not limited to); index
, source
, and sourcetype
.
sourcetype
defines how data is ingested and how fields are assigned. Splunk natively supports common sourcetypes as defined here. Generally sourcetypes define the fields applied to the events for extraction.
In many cases, these fields are defined in Splunk’s Common Information Model (CIM), which is useful in mapping the same fields from different sources (e.g. sourceip, srcip, ip -> src_ip).
At its most basic the Splunk query language supports a range of operators on fields, much like the other search languages covered in previous posts.
sourcetype="aws:cloudtrail" action="success"
I know the sourcetype aws:cloudtrail
uses the Authentication CIM (that contains the fieldname action
), as documented in the Splunk Add-on for AWS which defines the sourcetype.
Multiple fields are treated with AND operators. So logically this search is asking for data from AWS Cloud Trail and where the action field in the log is equal to success.
Other operators are available, for example NOT;
sourcetype="aws:cloudtrail" NOT action="success"
Now I am filtering out all events where action="success"
from Cloud Trail logs.
Another similarity SPL shares with Kusto is the use of the pipe |
character, and it works in just the same way – it passes (or ‘pipes’) the data through into the next command.
sourcetype="aws:cloudtrail" action="success" | stats count by action
In this command, first I get all events with action="success"
, and then I pipe that to a stats count
function. This counts the number of logs returned where action="success"
and returns the number as a count
field.
There are a huge variety search functions available in Splunk, in addition to stats.
You do not even need to search by field=value
. Although more inefficient in terms of performance, I can also do a plain text search on the logs;
success | stats count by action
This example will produce a count field, counting number of times the string “success” appears in the logs.
Importantly, you can also specify a time range using the fixed fields earliest
and latest
.
Here is an example of using a time range in a search that goes back 5 minutes, snapping to the beginning of the minute.
sourcetype="aws:cloudtrail" action="success" earliest=-5m@m latest=@m
There are a wide variety of time modifiers available to use in your queries.
I can also use other commands to filter the results, especially when I want to reduce false positives.
sourcetype="aws:cloudtrail" action="success" earliest=-5m@m latest=@m | stats count by action | where count > 10
Here I am looking for success actions within a 5 minute window, where there are more than 10 success actions are seen within the time window.
Sigma Rule to Splunk Detection
Now you know a little more about Splunk detections, lets try and manually create one from an existing Sigma Rule.
Sigma Rule
title: AWS User Login Profile Was Modified
id: 055fb148-60f8-462d-ad16-26926ce050f1
status: experimental
description: An attacker with the iam:UpdateLoginProfile permission on other users can change the password used to login to the AWS console on any user that already has a login profile setup.
With this alert, it is used to detect anyone is changing password on behalf of other users.
author: toffeebr33k
date: 2021/08/09
references:
- https://github.com/RhinoSecurityLabs/AWS-IAM-Privilege-Escalation
logsource:
product: aws
service: cloudtrail
detection:
selection_source:
eventSource: iam.amazonaws.com
eventName: UpdateLoginProfile
filter:
userIdentity.arn|contains: requestParameters.userName
condition: selection_source and not filter
fields:
- userIdentity.arn
- requestParameters.userName
- errorCode
- errorMessage
falsepositives:
- Legit User Account Administration
level: high
tags:
- attack.persistence
- attack.t1098
Splunk Detection
First I can map everything except for the detection;
name: AWS User Login Profile Was Modified
id: 055fb148-60f8-462d-ad16-26926ce050f1
description: An attacker with the iam:UpdateLoginProfile permission on other users can change the
password used to login to the AWS console on any user that already has a login profile setup.
With this alert, it is used to detect anyone is changing password on behalf of other users.
version: 1
date: '2021-08-09'
author: toffeebr33k
type: Hunting
datamodel:
- Change
known_false_positives: 'Legit User Account Administration'
references: ['https://github.com/RhinoSecurityLabs/AWS-IAM-Privilege-Escalation']
tags:
mitre_attack_id:
- T1098
sigma_level: high
sigma_fields:
- 'userIdentity.arn'
- 'requestParameters.userName'
- 'errorCode'
- 'errorMessage'
sigma_status: experimental
A lot of Sigma properties map one-to-one with the Splunk detection properties. I had to manually define the type
and datamodel
values. For everything else that did not map one-to-one I used the tags property to declare them using the prefix sigma_
.
Now all that is left is to write the search.
From the logsource I know the product is AWS Cloud Trail;
logsource:
product: aws
service: cloudtrail
Which maps to the sourcetype="aws:cloudtrail"
from the Splunk Add-on for AWS, which uses the CIMs Authentication and Change.
The Sigma Rule contains two Search Identifiers, selection_source
and filter
and a condition that states selection_source and not filter
.
This translates into a Splunk search;
sourcetype="aws:cloudtrail" action="updated" | where user!=src_user
I looked up the action
, user
and src_user
from the Splunk Change CIM
user
The user or entity performing the change. For account changes, this is the account that was changed. Seesrc_user
for user or entity performing the change. Fill out and normalize first to user & src_user, for example when using Assets and Identities in Enterprise Security.
Putting this together, it gives a final Splunk Detection YAML;
name: AWS User Login Profile Was Modified
id: 055fb148-60f8-462d-ad16-26926ce050f1
description: An attacker with the iam:UpdateLoginProfile permission on other users can change the
password used to login to the AWS console on any user that already has a login profile setup.
With this alert, it is used to detect anyone is changing password on behalf of other users.
version: 1
date: '2021-08-09'
author: toffeebr33k
type: Hunting
datamodel:
- Change
known_false_positives: 'Legit User Account Administration'
references: ['https://github.com/RhinoSecurityLabs/AWS-IAM-Privilege-Escalation']
tags:
mitre_attack_id:
- T1098
sigma_level: high
sigma_fields:
- 'userIdentity.arn'
- 'requestParameters.userName'
- 'errorCode'
- 'errorMessage'
sigma_status: experimental
search:
'sourcetype="aws:cloudtrail" action="updated" | where user!=src_user'
Which I can save as AWSUserLoginProfileWasModified.yaml
ready for use as a Splunk detection.
Useful links for writing Splunk Detection Content
Automating Rule Translation
In the next part of the tutorial I will show you how automate the manual process of converting a Sigma Rule into a target format using Sigmac.
Sigma Certification (Virtual and In Person)
The content used in this post is a small subset of our full training material used in our Sigma training.
If you want to join a select group of certified Sigma professionals, subscribe to our newsletter below to be notified of new course dates.
Discuss this post

Never miss an update
Sign up to receive new articles in your inbox as they published.