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
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. Unlike
description, 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 Stories
mitre_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 network
asset_type: Type of asset being investigated in string format. e.g AWS Instance
risk_object: see risk scoring
risk_object_type: see risk scoring
risk_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 one property I missed from the YAML structure above was the required
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);
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.
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
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
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[email protected]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[email protected]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.
Here is a public Sigma Rule detecting the deletion the modification of a user profile to detect anyone is changing password on behalf of other users.;
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
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
datamodel values. For everything else that did not map one-to-one I used the tags property to declare them using the prefix
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,
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
src_user from the Splunk Change CIM
userThe user or entity performing the change. For account changes, this is the account that was changed. See
src_userfor 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.