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 (usually, the interactive graphs). Please view the post on dogesec.com for the full interactive viewing experience.

tl;dr

As a detection engineer, you likely work with various SIEM or XDR tools.

This is problematic because it adds a large amount of friction to the sharing detection content.

What is needed is a detection rule standard. A way to write a query once and use it everywhere, on any system.

That is the ambitious aim of the Sigma project.

Sigma has been around for about three years. Though in the last year it has seen a marked increase in adoption.

Sigma is for log files what Snort is for network traffic and YARA is for files.

Sigma Github Repo

The Sigma Rule format has been designed to accommodate for conversion into other query languages to match the systems on which they will be used. For example, a Sigma Rule could be translated into Splunk’s query language, SPL, or Google Chronicle’s YARA-L 2.0 queries.

The aim of this post is to get you to a point where you’re comfortable with writing Sigma Rules.

How we ended up here

Our intelligence team first started considering Sigma when we ran into limitations with STIX Patterns (a STIX specific detection language).

I have previously written about writing STIX Patterns on the blog.

Whilst STIX Patterns allow for behavioural based detections, they quickly start running into limitations in more complex scenarios.

As an example, lets say we would like to detect the use of several reconnaissance commands in quick succession using STIX Patterns. Each command alone is not suspicious but together they may indicate reconnaissance activity.

For example;

([process:command_line LIKE ‘auser%’] AND [process:command_line LIKE ‘dir%’] AND [process:command_line LIKE ‘net%’]) WITHIN 30 SECONDS

Although this pattern may look like it is doing what we want, there is a major issue; there is no way to limit the scope of events this detection applies to.

When you’re monitoring the entire network what this really means is: auser anywhere AND dir anywhere AND net anywhere WITHIN 30 SECONDS. This detection is far too wide, it should really be more targetted applying this logic to the same hostname and/or potentially the same user within this time range.

Whilst STIX is the best solution for modelling threat intelligence, it is clearly not for detections.

Given the open standard, flexibility of rule creation, and ecosystem around Sigma, we quickly realised it was what we needed…

The Basics of Sigma Rules

Sigma Rules are structured in a YAML format.

The Sigma Rule v2.0 specification is split into three parts;

  1. Sigma Rules Specification: the core spec
  2. Sigma Filters Specification: how to apply filters to suppress matches across multiple Sigma Rules (e.g. useful for environment specific tuning where a false positive prone application is used in an organisation and its false positives are accepted.)
  3. Sigma Correlation Rules Specification: a more advanced way to create detection logic across multiple rules

In this post I’m going to focus on the core specification.

Preparing to write a rule

When writing Sigma Rules, I find it helpful to think of a Sigma Rules in sections;

Sigma Rule Structure

  1. Metadata: information that helps classify the rule
  2. Log sources: describes the log data on which the detection is meant to be applied to
  3. Detection: the logic to identify something in the log

1. Metadata

1.1 General info

The main purpose of these properties is to help manage and maintain rules.

My advice would be to take a look at some existing Sigma Rules to see some example values used for these properties, but here’s a short overview of them;

  • id (optional): a UUIDv4 to identify the rule
  • related (optional): To be able to keep track of the relationships between detections, Sigma rules may also contain references to related rule identifiers in the related attribute.
  • name (optional): a unique machine-readable name that can be used instead of the id as a reference in correlation rules (e.g. failed_logins)
  • title (required): A brief title for the rule that should contain what the rule is supposed to detect
  • description (optional): A short and accurate description of the rule and the malicious or suspicious activity that can be detected
  • status (optional): Declares the status of the rule. Dictionary of options available here.
  • license (optional): License of the rule according the SPDX ID specification.
  • author (optional): Creator of the rule. (can be a name, nickname, twitter handle…etc)
  • references (optional): References to the sources that the rule was derived from.
  • date (optional): Creation date of the rule.
  • modified (optional): Last modification date of the rule.
  • level (optional): The level field contains one of five values listed here. It describes the criticality of a triggered rule.
  • tags (optional): A Sigma rule can be categorized with tags, some are standardised

1.2 Versioning

To track when Sigma Rules are updated the date (the date the rule was created) and modified (the date it was last modified) values can be used in the format YYYY-MM-DD.

For example;

id: 929a690e-bef0-4204-a928-ef5e620d6fcc
title: Test rule
date: 2025-01-01
modified: 2022-01-01

Reasons to change the modified date:

  • changed title
  • changed detection section
  • changed level
  • changed logsource (rare)
  • changed status to deprecated

1.3 Linking rules

To keep track of relationships between rules, Sigma rules may also contain references to related rule ids along with the description of the relationships using the list of options here. For example;

id: 929a690e-bef0-4204-a928-ef5e620d6fcc
related:
  - id: 08fbc97d-0a2f-491c-ae21-8ffcfd3174e9
  type: derived
  - id: 929a690e-bef0-4204-a928-ef5e620d6fcc
  type: obsoletes

Here the current rule (929a690e-bef0-4204-a928-ef5e620d6fcc) is derived from another rule (08fbc97d-0a2f-491c-ae21-8ffcfd3174e9) and replaces (obsoletes) a rule (929a690e-bef0-4204-a928-ef5e620d6fcc).

This can also be used to link rules. For example, those detecting the same thing (e.g. a Vulnerability).

1.4 Tags

A Sigma rule can be categorised with tags. Tags can be anything, but Sigma ships with some predefined tags which I’d recommend you use where possible (or sending a pull request / creating an issue to the Sigma repo with proposals for new tags).

A tag ultimately provides more contextual information about the rule.

Tags are namespaced, (a . is used as separator, e.g. attack.t1059.001, here attack is the namespace).

Predefined Sigma tags include;

Here’s an example;

tags:
  - attack.defense_evasion
  - attack.t1027
  - attack.execution
  - attack.t1059.001
  - car.2016-04-005
  - tlp.amber
  - not.a-defined-tag

1.5 In summary

To demonstrate these properties, here is a partial rule (without log sources or the detection defined)

id: 929a690e-bef0-4204-a928-ef5e620d6fcc
related:
  - id: 08fbc97d-0a2f-491c-ae21-8ffcfd3174e9
  type: derived
  - id: 929a690e-bef0-4204-a928-ef5e620d6fcc
  type: obsoletes
title: Demo rule
description: Just showing off metadata properties
author: David Greenwood
date: 2025-01-01
modified: 2025-01-01
status: unsupported
license: Apache-2.0
level: informational
references:
      - https://www.dogesec.com
      - https://www.github.com/muchdogesec
tags:
  - attack.defense_evasion
  - attack.t1027

2. Log sources

The logsource attribute describes the log data on which the detection is meant to be applied. It is useful for the rule convertors to determine the indexes and data sources the detection should be applied to.

When I talk about convertors in this post, I am talking about code that translates a Sigma rule into another detection language (see: pySigma).

logsource is made up of the following attributes;

  • category:
    • used to select all log files written by a certain group of products, like firewalls or web server logs.
      • e.g. firewall, web, antivirus
  • product:
    • used to select all log outputs of a certain product, e.g. all Windows Eventlog types including “Security”, “System”, “Application” and the new log types like “AppLocker” and “Windows Defender”.
      • e.g. windows, apache
  • service:
    • used to select only a subset of a product’s logs, like the “sshd” on Linux or the “Security” Eventlog on Windows systems.
      • e.g. sshd, applocker
  • definition
    • used to describe the logsource, including some information on the log verbosity level or configurations that have to be applied (e.g. Script Block Logging must be enabled. Note, unlike the other logsource properties, definition is not considered by convertors and is designed to provide helpful information to those reading the rule.
      • e.g. INFO, DEBUG

You can use the values of category, product and service to point the converters to a certain index.

For example,

logsource:
  category: firewall

Then in the convertor configuration files, it can be defined that the category firewall converts to ( index=fw1* OR index=asa* ) during Splunk search conversion.

Or,

logsource:
  product: windows

Here, the product windows could be converted to "_index":"logstash-windows*" in Elasticsearch queries.

A list of standard logsources can be viewed here.

Generally, you’ll want to use a pre-existing logsource. Of course, if a log source does not exist for your log type, most likely when custom products, logs, or field naming is used, then you can specify a non-standard logsource using the logic defined.

Sigma does not restrict what a Sigma logsource can be defined as, meaning you can use Sigma for just about any kind of logsource within your SIEM.

Sigma compiles the logsource sub-attributes using AND statements. Take this example;

logsource:
  product: aws
  service: cloudtrail

Here it is saying the logsource must be; product: aws AND service: cloudtrail. This will then apply all settings for product and service defined in the convertors.

The advantages of this abstract approach is that it does not limit the rule to a specific telemetry source.

Instead creating multiple rules for the different telemetry sources such as Sysmon, Microsoft-Windows-Security-Auditing, Microsoft-Windows-Kernel-Process and all the other possible product-specific sources, a generic log source may be used which is then handled by the convertors.

e.g.

category: process_creation
product: windows

3. Detection

The detection section contains the core logic that identifies specific events and behaviours within the logsources deemed to something worth detecting.

The detection section contains a set of sub-attributes that represent searches on log data and how they should be evaluated:

  • Selections: What you actually wish to select/search from the log data
  • Conditions: How should the Selections or filters are to be evaluated

As a simple example, a selection could define a range of strings to search for in the logs (e.g. process IDs) while the condition could define how many of the process ID must be seen to trigger the detection.

This is a really simple example, both the selections and conditions can be used to perform much more advanced used cases.

Let me demonstrate…

3.1 Selections

3.1.1: Selections: Lists

Lists are the simplest way to define a selection. They contain strings that are applied to the full log message and are linked with a logical OR statement.

logsource:
  product: windows
  service: system
detection:
  selection:
    - 4728
    - 4729
    - 4730
  condition: selection

In this example, selection matches events containing 4728 OR 4729 OR 4730.

The naming of the attribute selection in this example is arbitrary (it could be anything).

For example, this detection would work in the same way;

logsource:
  product: windows
  service: system
detection:
  keywords:
    - 4728
    - 4729
    - 4730
  condition: keywords

Of course, it is a good idea that the selection attribute names are descriptive and obvious to the reader.

The rule above is simple, and very inefficient because no field names are defined. It is simply searching for a list of strings in the logs.

3.1.2: Selections: Field Lists

That’s where we can search by field list. For example, lets narrow the last rule down to only search for the specified values in a field called EventID

logsource:
  product: windows
  service: system
detection:
  selection:
    EventID:
      - 4728
      - 4729
      - 4730
  condition: selection

For example, now only the EventID field in downstream tools will be searched for the values 4728 OR 4729 OR 4730.

Note, field names are case sensitive. EventID and eventid are two different values.

You can also use key: value selections to search for single fields. For example,

logsource:
  product: windows
  service: system
detection: 
    selection:
        EventID: 6416
    condition: selection

You can also pass multiple fields (that are joined with an AND statement);

logsource:
  product: windows
  service: system
detection: 
    selection:
        EventID: 6416
        ClassName: DiskDrive
    condition: selection

In this example I am matching on any events where the EventID=6416 AND the ClassName=DiskDrive. I could add more field/values to filter on, if needed.

You can also combine these concepts together.

logsource:
  product: windows
  service: system
detection:
  selection:
    EventLog: Security
    EventID:
      - 517
      - 1102
condition: selection

Here the selection matches on Eventlog=Security AND ( EventID=517 OR EventID=1102).

3.1.3: Selections: Special values

You should be aware there are special field values that can be used for values.

  • An empty value is defined with ''
  • A non-existent value is defined with null

To demonstrate…

logsource:
  product: windows
  service: system
detection:
  selection:
    EventLog: Security
    EventID: ''
condition: selection

Would mean EventID field should be present, but have no value.

logsource:
  product: windows
  service: system
detection:
  selection:
    EventLog: Security
    EventID: null
condition: selection

Would mean the EventID should not be present.

3.1.4: Selections: Wildcards

You can also use wildcards in the value string. For example using * to replace an unbounded length wildcard;

logsource:
  product: windows
  service: system
detection:
  selection:
    EventLog: Security
    EventID: 5*
condition: selection

Would match on any EventID starting with 5 (e.g. 500, 5121, etc.)

? is used to replace a single mandatory character, for example;

logsource:
  product: windows
  service: system
detection:
  selection:
    FileName: prog?.exe
condition: selection

Would match on any FileName where the ? had a value (e.g. prog1.exe, prog2.exe, proga.exe but not rogram.exe)

The backslash character \ is used for escaping of wildcards * and ? as well as the backslash character itself. Escaping of the backslash is necessary if it is followed by a wildcard depending on the desired result.

For example, if you wanted to match on a value ? or *, you’d need to use a \ to show that it should not be used as a wildcard,

logsource:
  product: windows
  service: system
detection:
  keywords:
    - question\?
    - star\*
condition: selection

Would match on the literal values question? or star*.

Similarly, if a backslash is to be matched on (e.g. a Windows path) it also needs to be escaped.

logsource:
  product: windows
  service: system
detection:
  selection:
    FilePath: \\example.exe
condition: selection

Would match on the value \example.exe.

3.1.5: Selections: Modifiers

The values contained in Sigma rules can be modified by value modifiers. Value modifiers are appended after the field name with a pipe character | as separator and can also be chained, e.g. fieldname|mod1|mod2: value. The value modifiers are applied in the given order to the value.

Modifiers can make the selections much more flexible.

3.1.5.1: Selections: Modifiers: contains

contains which puts * wildcards around the values, such that the value is matched anywhere in the field. Here is an example;

detection:
  selection:
    CommandLine|contains:
      - DumpCreds
      - invoke-mimikatz
  condition: selection

This is the same as using:

detection:
  selection:
    CommandLine:
      - *DumpCreds*
      - *invoke-mimikatz*
  condition: selection

It’s also important to point out how modifiers are written – they are appended after the field name with a | character.

In the first example, the selection matches the CommandLine field in the log data and uses the Transformation Modifier contains in order to check if the keywords DumpCreds OR invoke-mimikatz are present in the field.

For example this detection would match on CommandLine="Has detected DumpCreds" OR CommandLine="DumpCreds" OR CommandLine="now invoke-mimikatz"

If I did not use the contains Value Modifier like so;

detection:
  selection:
    CommandLine:
      - DumpCreds
      - invoke-mimikatz
  condition: selection

Now only an exact match for the CommandLine field would match. This would be either CommandLine="DumpCreds" OR CommandLine="invoke-mimikatz". CommandLine="Has detected DumpCreds" would not match.

3.1.5.2: Selections: Modifiers: startswith / endswith

You might want to use a more specific Transformation Modifier, like startswith OR endswith.

detection:
  selection:
    CommandLine|startswith:
      - DumpCreds
      - invoke-mimikatz
  condition: selection

Here the CommandLine field in the event must start with either DumpCreds OR invoke-mimikatz.

detection:
  selection:
    CommandLine|endswith:
      - DumpCreds
      - invoke-mimikatz
  condition: selection

Here the CommandLine field in the event must end with either DumpCreds OR invoke-mimikatz.

3.1.5.3: Selections: Modifiers: all

The all Transformation Modifier can also prove very useful on occasion. As noted, Lists of values are treated by default using the logical OR statement. This modifier changes this to AND.

detection:
  selection|all:
    - DumpCreds
    - invoke-mimikatz
  condition: selection

In this example, I am now saying the levent must have both DumpCreds AND invoke-mimikatz in its value (note I don’t select a field name in this detection, as I did in previous examples).

3.1.5.4: Selections: Modifiers: exists

In some case a field can be optional in the event. You can use the exists modifiers to check it.

detection:
    selection:
        EventID: 4738
        PasswordLastSet|exists: true
    condition: selection

Checks the log contains a PasswordLastSet field.

exists is a boolean modified, it must be set to either true or false.

3.1.5.5: Selections: Modifiers: cased

Default Sigma behavior is case-insensitive matching, cased will observe the case of the value entered, e.g.

detection:
    selection:
        username|cased: david
    condition: selection

Will only match where the log contains a username value equal to david. It won’t match if it’s DAVID.

3.1.5.6: Selections: Modifiers: gt / gte / lt / lte

Greater than (gt), greater or equal to gte, less than (lt), and less than or equal to lte, are useful modifiers when working with numerical values.

detection:
    selection:
        port|gte: 8000
        port|lte: 9000
    condition: selection

Here the rule will match if the value for the port field is between 8000 and 9000

3.1.5.7: Selections: Modifiers: windash

Creates all possible permutations of the -, /, (en dash), (em dash), and (horizontal bar) characters.

This is incredibly useful in the the Windows ecosystem, where Windows has two standards for passing arguments to commands, usually - for PowerShell (e.g. -a), and / for cmd.exe (e.g. /a), but a large number of commands will commonly accept both. Many tools, including PowerShell, will not only accept a normal hyphen, but other similar looking dashes.

detection:
  selection:
    CommandLine|windash|contains:
      - '-s '
      - '-f '
      - '-t '
      - '-m '
      - '-a '
      - '-u '

Here -s would be searched as /s , -s , ―s etc, in the log. The same is true for other items in the list.

As you can see above, Modifiers can also be chained using a |, (e.g. fieldname|mod1|mod2:). The value modifiers are applied in the given order to the value.

This example logically reads the CommandLine field should be searched for one of the list items after the dash is replaced with a forward slash, and the search should consider that the field contains the converted value.

3.1.5.8: Selections: Modifiers: base64offset / base64

The base64 modifier-set will encode the provided values as base64 encoded strings. Often used alongside contains to identify malicious injection into applications.

detection:
  selection:
    fieldname|base64offset|contains:
      - /bin/bash
      - /bin/sh
      - /bin/zsh
  condition: selection

e.g. /bin/bash would be encoded as base64 and the base64 value (L2Jpbi9iYXNo) would what would be searched in the logs. So this search could be written as;

detection:
  selection:
    fieldname|contains:
      - L2Jpbi9iYXNo
      - L2Jpbi9zaA==
      - L2Jpbi96c2g=
  condition: selection
3.1.5.9: Selections: Modifiers: cidr

The cidr modifier allows for CIDR-formatted subnets to be used as field values, where any IPv4 or IPv6 addresses are supported.

detection:
  selection:
    first_ip_address|cidr: 192.0.0.0/8
    second_ip_address|cidr: 192.168.0.0/23
  condition: selection

This query will trigger if any IP addresses in range 192.0.0.0/8 (16,777,216 IP addresses) AND any IP addresses in the range 192.168.0.0/23 (512 IP addresses) are found in the event.

3.1.5.10: Selections: Modifiers: re

Sometimes specific do not quite suit what you are trying to achieve, particularly with more complex/varying values that need to be detected.

You can use the Regular Expression (re) modifier for pattern based matching. Here is an example of it being used;

detection:
  search_identifier_1:
    - CommandLine|re: '\$PSHome\[\s*\d{1,3}\s*\]\s*\+\s*\$PSHome\['
    - CommandLine|re: '\$ShellId\[\s*\d{1,3}\s*\]\s*\+\s*\$ShellId\['
    - CommandLine|re: '\$env:Public\[\s*\d{1,3}\s*\]\s*\+\s*\$env:Public\['
  condition: search_identifier_1

Here the event CommandLine field values must match at least one of the Regular Expressions defined.

You might be tempted to use the Regular Expressions Type Modifier a lot, though avoid it where possible (as it can create downstream conversion issues).

3.1.5.11: Selections: Modifiers: more modifiers

I’ve only covered the most common modifier above. Other options for more advanced use-case are;

  • expand
  • fieldref
  • utf16 / utf16le / utf16be / wide

You can read more about these here.

3.1.6 What field name do I use?

Theoretically you can use whatever field names you wish, as long as someone is willing to put in the time to write a Sigma taxonomy to define the fields.

By default the Sigma taxonomy, defined here will be used.

If your logsource and/or field name is not listed there, you will need to create a custom taxonomy.

A taxonomy can define:

  • field names, example: process_command_line instead of CommandLine.
  • field values, example: a field image_file_name that only contains a file name like example.exe and is transformed into ImageFile: *\\example.exe.
  • logsource names, example: category: ProcessCreation instead of category: process_creation

The custom taxonomy can be defined in the rule itself under the taxonomy property.

I won’t go into any more detail in this post, as I’d consider this fairly advanced usage of Sigma. The default Sigma taxonomy is broad enough to get you started.

3.2 Conditions

So far I’ve only covered one selection in a rule. In fact, a Sigma Rule detection can have many selections.

That’s where conditions come into play.

For example;

detection:
  selection_1:
    Image|endswith: '/bash'
    CommandLine|contains:
      - DumpCreds
      - invoke-mimikatz
  selection_2:
    CommandLine|contains:
      - rpc
      - token
      - crypto
  selection_3:
    CommandLine|contains:
      - bitcoin
  condition: selection_1 OR selection_2 OR selection_3

Here I use three selections in the condition.

The condition property defines the combinations of selections that should trigger a detection.

In this case, if one of the three conditions (selection_1 OR selection_2 OR selection_3) is true, then a detection should be triggered.

Essentially this detection reads;

(`Image|endswith = '/bash'` AND (`CommandLine CONTAINS DumpCreds` OR `CommandLine CONTAINS invoke-mimikatz`)) OR 
(`CommandLine CONTAINS rpc` OR `CommandLine CONTAINS token` OR `CommandLine CONTAINS crypto`) OR
(`CommandLine CONTAINS bitcoin`)

Conditions can be defined using a variety of operators.

Operators that can prove useful in tuning the extensibility and accuracy a rule by describing how the selections should be considered. We’ve already covered two…

  1. Exact match
    • e.g. selection_1
  2. Logical AND/OR
    • e.g. selection_1 OR selection_2
    • e.g. selection_1 AND selection_2

Other options include…

3.2.1.1 Conditions: 1/all of them

Before I showed the example condition: selection_1 OR selection_2 OR selection_3.

This could actually be written in a simpler way…

detection:
  selection_1:
    Image|endswith: '/bash'
    CommandLine|contains:
      - DumpCreds
      - invoke-mimikatz
  selection_2:
    CommandLine|contains:
      - rpc
      - token
      - crypto
  selection_3:
    CommandLine|contains:
      - bitcoin
  condition: 1 of them

To denote at least 1 of the selections (1 of them) should be true (selection_1 OR selection_2 OR selection_3). Ultimately, this is the same detection ion logic as the last example.

However, you can also use all of them to denote all selections should match.

detection:
  selection_1:
    CommandLine|contains:
      - DumpCreds
      - invoke-mimikatz
  selection_2:
    CommandLine|contains:
      - rpc
      - token
      - crypto
  selection_3:
    CommandLine|contains:
      - bitcoin
  condition: all of them

In this example, not all selections must match. Written out;

(Image|endswith = '/bash' AND (CommandLine CONTAINS DumpCreds OR CommandLine CONTAINS invoke-mimikatz))
AND
(CommandLine CONTAINS rpc OR CommandLine CONTAINS token OR CommandLine CONTAINS crypto)
AND
(CommandLine CONTAINS bitcoin)

Note, the Sigma docs advise against using 1 of them or all of them.

3.2.2.2 Conditions: 1/all of selection

Instead of X of them you can be more granular and include parts of a selections in the condition (vs. the entire selection). For example;

logsource:
  product: windows
  service: system
detection:
  selection_1:
    EventLog: Security
    EventID:
      - 517
      - 1102
  selection_2:
      EventID: 6416
      ClassName: DiskDrive
condition: all of selection_1 and 1 of selection_2

Here the condition defines all of selection_1 must match, but only 1 field in selection_2 should. Written out this reads;

(EventLog = Security AND ( EventID = 517 OR EventID = 1102 ))
AND
(EventID = 6416 OR ClassName = DiskDrive)

Here instead of the default operator behaviour of a selection (to AND each field), the condition logic for selection_2 turns this into an OR.

* wildcards (i.e. any number of characters) at arbitrary positions in the condition pattern can also be used. For example;

logsource:
  product: windows
  service: system
detection:
  selection_1:
    EventLog: Security
    EventID:
      - 517
      - 1102
  selection_2:
      EventID: 6416
      ClassName: DiskDrive
  condition: 1 of selection_* 

Written literally it is saying 1 item in each selection must match;

( EventLog = Security OR ( EventID = 517 OR EventID = 1102 ))
AND
( EventID = 6416 OR ClassName = DiskDrive )

3.2.3.3 Conditions: Negation with not

Conditions can be especially useful for filtering use-cases which is where the not operator comes in handy. Take this example;

logsource:
  product: windows
  service: system
detection:
  selection_1:
    EventLog: Security
    EventID:
      - 517
      - 1102
  selection_2:
      EventID: 6416
      ClassName: DiskDrive
  keywords_filter|contains:
    - error
    - failure
  condition: 1 of selection_* not keywords_filter

The condition is almost identical to the last example, but is now filtering out any events that contain error OR failure.

( EventLog = Security OR ( EventID = 517 OR EventID = 1102 ))
AND
( EventID = 6416 OR ClassName = DiskDrive )
AND NOT
( <the event contains the string 'error' OR 'failure' >
A side not on filtering events using other techniques

Filtering techniques are often common across multiple rules (e.g. the keyword error used in the last rule is common in all kinds of logs). Instead of writing this filter into every rule, you can use Sigma Filter rules so that the convertors apply them across multiple rules in one command.

Sigma Filter rules are another type of Sigma rule; meta rules.

Sigma Filters closely resemble core Sigma rules in their structure, but use the filter keyword in place of detection.

For example win_filter_domain_admins.yml;

title: Filter Out Domain Controllers
description: Filter out events from Domain Controllers
logsource:
    product: windows
filter: 
  rules:
    - files_added_to_an_archive_using_rar_exe
    - login_on_jump_host
  selection:
    ComputerName|startswith: "DC-"
  condition: not selection

This filter should be added to the following rules by the convertor;

  • files_added_to_an_archive_using_rar_exe.yml (rule)
  • login_on_jump_host.yml (rule)

Lets work this through using another example.

Here’s the Sigma filter win_filter_admins.yml;

title: Filter Out Administrator accounts
description: Filters out administrator accounts that start with adm_
logsource:
  category: process_creation
  product: windows
filter:
  rules:
    - proc_creation_win_sc_create_service
  selection:
    User|startswith: "adm_"
  condition: not selection

And the core Sigma rule listed in the filter rule;

title: New Service Creation Using Sc.EXE
name: proc_creation_win_sc_create_service
description: Detects the creation of a new service using the "sc.exe" utility.
author: Timur Zinniatullin, Daniil Yugoslavskiy, oscd.community
logsource:
  category: process_creation
  product: windows
detection:
  selection:
    Image|endswith: '\sc.exe'
    CommandLine|contains|all:
      - "create"
      - "binPath"
  condition: selection
falsepositives:
  - Legitimate administrator or user creates a service for legitimate reasons.
  - Software installation
level: low

In the example above, the win_filter_admins.yml Sigma filter is applied to the proc_creation_win_sc_create_service.yml Sigma rule. The filter will exclude any events where the User field starts with adm_.

If you wanted to apply this filter to other rules in the future, you could just add that rule to the rules attribute in the Sigma Filter.

3.2.4.4 Conditions: Using Parenthesis

Parenthesis can be used to add more complex logic to the expression. The part of the condition in parenthesis will be considered first. For example;

As conditions need to become more complex, using brackets (parenthesis) can offer additional options to the other Condition options specified.

detection:
  keywords_1:
    - DumpCreds
    - invoke-mimikatz
  keywords_2:
    - rpc
    - token
    - crypto
  keywords_3:
     - bitcoin
  condition: (keywords_1 and keywords_2) or (keywords_2 and keywords_3)

In this example, if 1 value from keywords_1 and keywords_2 OR 1 value from keywords_2 and keywords_3 is seen in the same event it will trigger a detection.

Bracket are considered with the highest order of operation by downstream Sigma tooling.

3.2.3 Dealing with false positive detections

Once you deploy a rule to one of your security tools analysts will also start to discover some incorrect detections.

You can try to avoid false positive detections by tuning the rule to ignore known erroneous triggers using a filter, for example…

detection:
  selection:
    - 'rm /var/log/syslog'
    - 'rm -r /var/log/syslog'
    - 'rm -f /var/log/syslog'
    - 'rm -rf /var/log/syslog'
    - 'mv /var/log/syslog'
    - ' >/var/log/syslog'
    - ' > /var/log/syslog'
  false_positives:
    - '/syslog.'
  condition: selection and not false_positives

However, it’s not always possible to account for all reasons false positives occur.

This is where the informational falsepositive property can be used to describe a list of known false positives which can occur from a detection.

detection:
  selection:
    - 'rm /var/log/syslog'
    - 'rm -r /var/log/syslog'
    - 'rm -f /var/log/syslog'
    - 'rm -rf /var/log/syslog'
    - 'mv /var/log/syslog'
    - ' >/var/log/syslog'
    - ' > /var/log/syslog'
  false_positives:
    - '/syslog.'
  condition: selection and not false_positives
falsepositives:
  - PIM (Privileged Identity Management) generates this event each time 'eligible role' is enabled.
  - Legitimate administration activities

This doesn’t affect the detection. However, when the rule is triggered, the falsepositives information inside the rule can help an analyst triaging alerts as to how they proceed (or if they follow up on it at all).

3.2.4 Dealing with detections

When a rule is deployed, it is only a matter of time before it triggers a detection. When that happens, the analyst handling the detection event might want to start a deeper investigation.

The fields property inside a Sigma Rule can help an analyst decide the next steps by defining a log fields that could be interesting in further analysis of the event.

detection:
  selection:
    - 'rm /var/log/syslog'
    - 'rm -r /var/log/syslog'
    - 'rm -f /var/log/syslog'
    - 'rm -rf /var/log/syslog'
    - 'mv /var/log/syslog'
    - ' >/var/log/syslog'
    - ' > /var/log/syslog'
  false_positives:
    - '/syslog.'
  condition: selection and not false_positives
falsepositives:
  - PIM (Privileged Identity Management) generates this event each time 'eligible role' is enabled.
  - Legitimate administration activities
fields:
  - CommandLine
  - ParentCommandLine

4. Some Examples

If you’re like me, it is much more helpful to learn from real-world examples.

The Sigma project maintains a body of public rules in the core Sigma repository here. These are very useful starting point when trying to structure a report (and to see if any rules closely match your hypothesis).

4.1 Example 1

Studying this rule that detects CACTUSTORCH activity;

title: HackTool - CACTUSTORCH Remote Thread Creation
id: 2e4e488a-6164-4811-9ea1-f960c7359c40
status: test
description: Detects remote thread creation from CACTUSTORCH as described in references.
references:
    - https://twitter.com/SBousseaden/status/1090588499517079552 # Deleted
    - https://github.com/mdsecactivebreach/CACTUSTORCH
author: '@SBousseaden (detection), Thomas Patzke (rule)'
date: 2019-02-01
modified: 2023-05-05
tags:
    - attack.defense-evasion
    - attack.execution
    - attack.t1055.012
    - attack.t1059.005
    - attack.t1059.007
    - attack.t1218.005
logsource:
    product: windows
    category: create_remote_thread
detection:
    selection:
        SourceImage|endswith:
            - '\System32\cscript.exe'
            - '\System32\wscript.exe'
            - '\System32\mshta.exe'
            - '\winword.exe'
            - '\excel.exe'
        TargetImage|contains: '\SysWOW64\'
        StartModule: null
    condition: selection
falsepositives:
    - Unknown
level: high

This rule should be applied to Windows product logs that can create remote threads (this is a standard Sigma logsource grouping).

The logic can be described literally as:

  • the SourceImage field in the event must end with \System32\cscript.exe OR \System32\wscript.exe OR \System32\mshta.exe OR \winword.exe OR \excel.exe AND
  • the TargetImage field in the event must contain \SysWOW64\ AND
  • the StartModule field must not be in the event

You can also see this rule has been tagged with ATT&CK Tactics and Techniques.

4.2 Example 2

title: Cicada3301 Ransomware Execution via PSExec
id: 79495647-d84d-4804-9a52-5263cfdf2c63
status: experimental
description: |
    Detects the use of a potentially-renamed psexec to run the Cicada3301 ransomware tool.
references:
    - https://engage.morphisec.com/threat-analysis-cicada3301
author: 'Micah Babinski, Based on Morphisec report by Michael Gorelik (@smgoreli)'
date: 2024-09-08
tags:
    - attack.execution
    - attack.t1569
    - attack.t1569.002
    - attack.s0029
logsource:
    category: process_creation
    product: windows
detection:
    selection_1:
        - Image|endswith: '\psexec0.exe'
        - OriginalFileName: 'psexec.c'
    selection_2:
        CommandLine|contains:
            - '--key'
            - '--path'
            - '-p '
            - '-s '
            - '--no_local'
            - '--no_net'
            - '--no_impl'
            - '--no_notes'
    filter:
        Image|endswith: '\psexec.exe'
    condition: all of selection_1 and selection_2 and not filter
falsepositives:
    - Unknown
level: high

This rule should be applied to Windows product logs that can create processes (again, this is a standard Sigma logsource grouping).

The logic can be described literally as:

  • the Image field must end with \psexec0.exe and have the OriginalFileName value as psexec.c AND
  • the CommandLine field must contain one of the field values for CommandLine listed in selection_2 (e.g. --key) AND
  • the Image field must NOT end with \psexec.exe

In summary

This post has given you enough to get started authoring your own basic Sigma detections.

To really make use of what I’ve covered, start putting it into practice.

Define some hypotheses and try and write Sigma detections for them.


SIEM Rules

Your detection engineering AI assistant. Turn cyber threat intelligence research into highly-tuned detection rules.

SIEM Rules. Your detection engineering database.

Discuss this post

Head on over to the dogesec community to discuss this post.

dogesec community

Posted by:

David Greenwood

David Greenwood, Do Only Good Everyday



Never miss an update


Sign up to receive new articles in your inbox as they published.

Your subscription could not be saved. Please try again.
Your subscription has been successful.