Detection Engineering

Sigma rules for log detection

Write vendor-neutral Sigma detection rules in YAML and convert them to Splunk, Elastic or Microsoft Sentinel KQL with sigma-cli.

1 Introduction

Every SIEM has its own query language: SPL for Splunk, KQL for Microsoft Sentinel and Defender, Lucene or ES|QL for Elastic, and so on. A detection written for one cannot be used on another, which makes sharing detections between teams painful. Sigma solves this: it is an open, vendor-neutral format for log detection rules, written in YAML, that a converter translates into the query language of your SIEM.

Created in 2017 by Florian Roth and Thomas Patzke, Sigma is to logs what YARA is to files and Snort/Suricata are to network traffic. Instead of an IOC that expires in days, a good Sigma rule describes a behaviour (a Word document that spawns PowerShell, a system tool used to download a file), the top of the Pyramid of Pain.

1 rule

converted to Splunk, Elastic, Sentinel, QRadar and many more

3,000+

community rules in the SigmaHQ repository

YAML

plain text, easy to review, version and share

How it works

1. You write a generic rule

Using generic field names (Image, CommandLine, ParentImage…) and a generic log category (process_creation).

2. A pipeline maps it to your data

A processing pipeline renames fields and adds the right index or table for your environment (e.g. Image becomes process.executable in Elastic ECS).

3. A backend writes the query

The backend outputs SPL, KQL, Lucene, ES|QL… that you paste into a search or deploy as a scheduled alert.

2 Rule anatomy

This rule detects certutil.exe, a legitimate Windows certificate tool, being abused to download a file (MITRE ATT&CK T1105, Ingress Tool Transfer). The comments explain each field.

proc_creation_win_certutil_download.yml

title: File Download via Certutil                # short, human-readable name
id: 3f2a8c1e-5b7d-4e9a-9c61-2d8f0a7b4c13         # random UUIDv4, never reused
status: experimental                             # experimental -> test -> stable
description: Detects certutil.exe used with the urlcache option to fetch a remote file, a classic living-off-the-land download technique.
references:
    - https://attack.mitre.org/techniques/T1105/
author: mlab Academy
date: 2026-10-21                                 # YYYY-MM-DD
tags:                                            # MITRE ATT&CK tactic + technique
    - attack.command-and-control
    - attack.t1105
logsource:                                       # WHICH logs this rule applies to
    category: process_creation
    product: windows
detection:                                       # WHAT to look for
    selection_img:                               # a list = OR between its items
        - Image|endswith: '\certutil.exe'
        - OriginalFileName: 'CertUtil.exe'
    selection_cli:                               # keys in a map = AND
        CommandLine|contains|all:                # |all = every value must be present
            - 'urlcache'
            - 'http'
    condition: all of selection_*                # how the named blocks combine
falsepositives:
    - Administrators manually retrieving certificates or CRLs over HTTP
level: high                                      # informational, low, medium, high, critical

Metadata fields

Field What to put in it
titleWhat is detected, in a few words. Mandatory.
idA random UUID (generate one with uuidgen). It identifies the rule forever, even if the title changes.
statusMaturity: experimental (new), test (tested, few known false positives), stable (safe to alert on), plus deprecated and unsupported.
description / referencesWhy the activity is suspicious and where it was documented (blog, CTI report, ATT&CK page).
author / date / modifiedWho wrote it and when, as YYYY-MM-DD.
tagsMITRE ATT&CK tactics and techniques (attack.execution, attack.t1059.001), CVEs (cve.2021-44228), TLP…
falsepositivesKnown legitimate causes. Precious for the analyst who triages the alert.
levelSeverity: informational, low, medium, high, critical.

logsource: which logs?

logsource combines up to three keys: product (windows, linux, aws, azure…), service (security, sysmon, powershell, cloudtrail…) and category (a generic type such as process_creation, network_connection, dns_query, file_event). Categories are powerful: process_creation on Windows can be fed by Sysmon event 1, Windows Security event 4688 or an EDR. The pipeline decides which data source is queried.

detection: selections, modifiers and condition

The detection block contains named search identifiers (any name you like: selection, filter_admin…) and one condition that combines them. Two rules are enough to read any of them:

Keys of a map = AND

In selection_cli-like blocks, every field listed must match.

Items of a list = OR

A list of values (or a list of maps, as in selection_img) matches if any item matches.

Values are case-insensitive by default, and * / ? are wildcards. Field modifiers, chained with |, change how a value is compared:

Modifier Effect
|contains / |startswith / |endswithSubstring, prefix and suffix matches (wildcards added for you).
|allTurns the OR of a value list into an AND: every value must be present.
|reRegular expression (case-sensitive unless you add |re|i). Slow on most SIEMs: use sparingly.
|windashAlso matches /flag and Unicode dashes when you write -flag: Windows tools accept them all.
|base64offset|containsFinds a string hidden inside base64 data, whatever its alignment.
|cidrIP ranges such as 10.0.0.0/8.
|gt / |gte / |lt / |lteNumeric comparisons.
|existsThe field is present (true) or absent (false).
|casedMakes the comparison case-sensitive.

The condition line accepts:

selection and not filterBoolean logic with and, or, not and parentheses.
1 of selection_*At least one identifier whose name matches the pattern.
all of selection_*All identifiers matching the pattern.
1 of them / all of themSame, over every identifier in the rule.

3 Worked examples

Step 1: one event, one field

The simplest possible rule: every failed Windows logon (Security event 4625). Far too noisy to alert on by itself, hence level: low, but it is a perfect building block for step 4.

win_security_failed_logon.yml

title: Windows Failed Logon
id: 0c7a1f2e-3b4d-4e5f-8a6b-7c8d9e0f1a2b
status: test
description: Demo rule - matches every failed logon on a Windows host.
author: mlab Academy
date: 2026-10-21
logsource:
    product: windows
    service: security
detection:
    selection:
        EventID: 4625
    condition: selection
level: low

Step 2: two selections combined

The certutil rule from section 2. selection_img identifies the binary twice, by path and by OriginalFileName (taken from the PE header, so it still matches if the attacker renames certutil.exe to notepad.exe). selection_cli requires both keywords.

Step 3: parent/child behaviour with a filter

A classic of malicious macro documents: an Office application starts PowerShell with an encoded command. We add a filter for a (fictional) approved add-in: filters are how you tune a rule for your environment without weakening it everywhere else.

proc_creation_win_office_spawn_encoded_powershell.yml

title: Encoded PowerShell Spawned by an Office Application
id: 9b1e4d27-6c3a-4f8e-b2d5-1a7c0e9f3b64
status: experimental
description: Detects Word, Excel or PowerPoint launching PowerShell with an encoded command, typical of malicious macro documents.
references:
    - https://attack.mitre.org/techniques/T1059/001/
    - https://attack.mitre.org/techniques/T1204/002/
author: mlab Academy
date: 2026-10-21
modified: 2026-10-21
tags:
    - attack.execution
    - attack.t1059.001
    - attack.t1204.002
logsource:
    category: process_creation
    product: windows
detection:
    selection_parent:
        ParentImage|endswith:
            - '\winword.exe'
            - '\excel.exe'
            - '\powerpnt.exe'
    selection_child:
        Image|endswith:
            - '\powershell.exe'
            - '\pwsh.exe'
    selection_cli:
        CommandLine|contains|windash:
            - ' -enc '
            - ' -encodedcommand '
    filter_main_approved_addin:
        CommandLine|contains: 'C:\Program Files\Contoso\ApprovedAddin\'
    condition: all of selection_* and not 1 of filter_main_*
falsepositives:
    - Rare internal add-ins that legitimately call encoded PowerShell (add them to a filter)
level: high

Exercise: PowerShell accepts any unambiguous prefix of a parameter, so -e, -ec or -enco also work. How would you extend selection_cli without matching every command line containing "-e"? (Hint: look at how the SigmaHQ rules handle it.)

Step 4: a correlation rule (brute force)

Since version 2.0 of the specification, Sigma supports correlation rules that count or link events from other rules. Here we reuse the step 1 rule (referenced by its name) and alert on ten failures from the same IP within five minutes. Both documents live in the same file, separated by ---.

win_security_failed_logon_bruteforce.yml

title: Windows Failed Logon
id: 0c7a1f2e-3b4d-4e5f-8a6b-7c8d9e0f1a2b
name: windows_failed_logon
status: test
description: Demo rule - matches every failed logon on a Windows host.
author: mlab Academy
date: 2026-10-21
logsource:
    product: windows
    service: security
detection:
    selection:
        EventID: 4625
    condition: selection
level: low
---
title: Many Failed Logons From One Source
id: 5e2d8b1a-7f4c-4a3e-9d6b-0c1f2a3b4c5d
status: experimental
description: Ten or more failed logons from the same source IP within five minutes.
author: mlab Academy
date: 2026-10-21
correlation:
    type: event_count
    rules:
        - windows_failed_logon
    group-by:
        - IpAddress
    timespan: 5m
    condition:
        gte: 10
level: medium
$ sigma convert -t splunk -p splunk_windows win_security_failed_logon_bruteforce.yml
source="WinEventLog:Security" EventCode=4625

| bin _time span=5m
| stats count as event_count by _time IpAddress

| search event_count >= 10

Other correlation types: value_count (e.g. one source trying many different user names), temporal and temporal_ordered (several rules firing together, or in a given order). Not every backend supports them yet.

4 Converting rules

The reference toolchain is pySigma (a Python library) and its command-line front-end sigma-cli. The old sigmac converter is deprecated: if a tutorial uses it, it is out of date.

# Install the CLI and the backends you need
$ pip install sigma-cli
$ sigma plugin install splunk
$ sigma plugin install elasticsearch
$ sigma plugin install kusto

# Lint your rules (syntax + style checks)
$ sigma check rules/

# See which targets and pipelines are available
$ sigma list targets
$ sigma list pipelines splunk

A conversion always needs a target (-t, the query language) and usually a pipeline (-p, the field mapping for your data). Here is the certutil rule converted for three SIEMs, real output of sigma-cli:

$ sigma convert -t splunk -p splunk_windows proc_creation_win_certutil_download.yml
Image="*\\certutil.exe" OR OriginalFileName="CertUtil.exe" CommandLine="*urlcache*" CommandLine="*http*"

$ sigma convert -t lucene -p ecs_windows proc_creation_win_certutil_download.yml
(process.executable.caseless:*\\certutil.exe OR process.pe.original_file_name:CertUtil.exe) AND (process.command_line:*urlcache* AND process.command_line:*http*)

$ sigma convert -t kusto -p microsoft_xdr proc_creation_win_certutil_download.yml
DeviceProcessEvents
| where (FolderPath endswith "\\certutil.exe" or ProcessVersionInfoOriginalFileName =~ "CertUtil.exe") and (ProcessCommandLine contains "urlcache" and ProcessCommandLine contains "http")
SIEM Target Common pipelines
Splunksplunksplunk_windows, splunk_cim
Elasticlucene, eql, esqlecs_windows, ecs_zeek_beats
Microsoft Defender XDRkustomicrosoft_xdr
Microsoft Sentinelkustosentinel_asim, azure_monitor, microsoft_xdr

The microsoft_xdr pipeline targets the advanced hunting tables (DeviceProcessEvents…), which are also available in Sentinel when the Defender XDR connector is enabled; sentinel_asim targets Sentinel's normalised ASIM tables instead. For the step 3 rule, the KQL looks like this. Notice how |windash expanded each flag into five variants:

DeviceProcessEvents
| where ((InitiatingProcessFolderPath endswith "\\winword.exe" or InitiatingProcessFolderPath endswith "\\excel.exe" or InitiatingProcessFolderPath endswith "\\powerpnt.exe") and (FolderPath endswith "\\powershell.exe" or FolderPath endswith "\\pwsh.exe") and (ProcessCommandLine contains " -enc " or ProcessCommandLine contains " /enc " or ProcessCommandLine contains " –enc " or ProcessCommandLine contains " —enc " or ProcessCommandLine contains " ―enc " or ProcessCommandLine contains " -encodedcommand " or ProcessCommandLine contains " /encodedcommand " or ProcessCommandLine contains " –encodedcommand " or ProcessCommandLine contains " —encodedcommand " or ProcessCommandLine contains " ―encodedcommand ")) and (not(ProcessCommandLine contains "C:\\Program Files\\Contoso\\ApprovedAddin\\"))

Read the generated query

Never deploy a conversion blindly. In the Splunk output above, OR has no parentheses: that is correct, because in SPL OR binds tighter than the implicit AND, but it surprises everyone the first time. Check field names and the index too, then run the query over the last 30 days to see how many results it returns before you turn it into an alert.

No SIEM? Run Sigma directly on log files

Several free tools apply Sigma rules straight to Windows event logs (.evtx), ideal for practising or for incident response on a single machine: Hayabusa (Yamato Security), Chainsaw (WithSecure) and Zircolite. The web converter sigconverter.io, built on pySigma, lets you try conversions without installing anything.

Turn a report into detections

Paste a threat report or a log excerpt into the mlab.sh IOC extractor to pull out the domains, IPs and hashes worth hunting for in your SIEM.

Extract IOCs on mlab.sh

5 Common pitfalls

The logs do not exist

A process_creation rule is useless if you collect neither Sysmon nor event 4688, and 4688 only contains the command line if "Include command line in process creation events" is enabled by GPO. Check the data before blaming the rule.

Wrong pipeline, silent failure

If the generated query uses Image but your index calls it process_path, the search runs without error and never matches anything. Test every converted rule against a known-positive event.

Backslashes in double quotes

In YAML, "\certutil.exe" in double quotes is an escape sequence and breaks the file. Use single quotes for Windows paths: '\certutil.exe'.

Rules that describe a tool, not a behaviour

"powershell.exe started" fires thousands of times a day. Add what makes it suspicious: the parent process, a flag, a path, a user. Alert fatigue kills a SOC faster than missed detections.

Filters that are too broad

Excluding CommandLine|contains: 'update' to silence one noisy tool also hands attackers a free bypass. Filter on full paths, signed images or specific parent processes.

Treating community rules as plug-and-play

An experimental rule from SigmaHQ was written for someone else's environment. Run new rules in "hunting" mode (no alert) for a week, tune them, then promote them.

Performance tips

Prefer endswith and exact values over contains

A leading wildcard (*urlcache*) prevents most SIEMs from using their index. Use it on the field that really needs it, not everywhere.

Keep a precise logsource

The logsource tells the pipeline which index or table to query. A rule without one may scan all your data.

Avoid |re when a modifier does the job

Regular expressions are expensive and their dialect differs between backends. |contains|all, |windash or a list of values are faster and more portable.

6 Going further

Where to find rules

SigmaHQ/sigma

The official repository. rules/ holds generic detections, rules-emerging-threats/ rules tied to specific campaigns or CVEs, rules-threat-hunting/ broader rules meant for hunting, not alerting. Ready-made release packages are published on GitHub.

SigmaHQ specification

The sigma-specification repository documents every field, modifier, taxonomy name and the correlation format. Bookmark it.

Vendor and researcher repos

Many threat reports now ship Sigma rules in their appendix, and threat intel feeds and MISP events can carry them too.

Detection-as-code

Store your rules in Git, run sigma check and conversions in CI, and deploy automatically to your SIEM. Each change is reviewed like code.

Connecting the dots

Sigma tags map every rule to MITRE ATT&CK, which lets you draw a coverage heat map and spot the techniques you cannot see. Behaviour observed in a sandbox (process tree, command lines) is the best raw material for new rules. For indicators that change daily, keep using IOC lookups and STIX/TAXII feeds; for files, use YARA; for network traffic, Suricata and Snort.

Related Modules