Sigma is a vendor-neutral detection rule format. Write a rule once, convert it to Splunk SPL, Elastic EQL, Microsoft Sentinel KQL, or QRadar AQL using sigma-cli. The promise of write-once deploy-anywhere is real, but only if you understand the format well enough to write rules that actually fire on real attacker behaviour rather than rules that look correct but miss everything in production.
Understanding the rule structure
A Sigma rule has a small number of required fields and a detection block that does the actual work. The logsource section tells the converter what data source the rule applies to. The detection section defines what to match. The condition section combines detection components with boolean logic. Everything else is metadata.
title: Suspicious Scheduled Task Creation
id: 92a5a95a-cce6-4b88-a5d0-c578d0d20d7e
status: stable
description: Detects scheduled task creation from unusual parent processes
author: justruss
date: 2025/01/09
tags:
- attack.persistence
- attack.t1053.005
logsource:
product: windows
category: process_creation
detection:
selection:
Image|endswith: '\schtasks.exe'
CommandLine|contains: '/create'
filter_legit:
ParentImage|startswith:
- 'C:\Windows\System32\'
- 'C:\Windows\SysWOW64\'
- 'C:\Program Files\'
condition: selection and not filter_legit
falsepositives:
- Software installers
- IT management tooling
level: medium
Converting with sigma-cli
pip install sigma-cli pysigma-backend-splunk pysigma-backend-elasticsearch
# Convert to Splunk SPL
sigma convert -t splunk -p splunk_windows rule.yml
# Convert to Elastic EQL
sigma convert -t elasticsearch -p ecs_windows rule.yml
# Convert to Microsoft Sentinel KQL
sigma convert -t microsoft365defender rule.yml
# Verify the output looks correct before deploying
sigma convert -t splunk -p splunk_windows rule.yml | head -20
Field modifiers: the most important thing to understand
Field modifiers change how a value is compared. Getting them wrong produces rules that either fire on nothing or fire on everything. The most commonly used modifiers are contains (substring match anywhere in the field value), startswith (match at the beginning), endswith (match at the end), re (full regex match), and all (all listed values must be present, rather than any one). Understanding when to use each one is the difference between a rule that catches the attack and one that misses it.
# Example: catching encoded PowerShell
detection:
selection:
CommandLine|contains|all:
- 'powershell'
- '-enc' # The |all modifier requires BOTH strings present
# Without |all, either string alone would match
# Example: catching multiple tools with one rule
detection:
selection:
Image|endswith:
- '\mimikatz.exe'
- '\procdump.exe'
- '\nanodump.exe'
# This is an OR - any one of these matches
The tuning workflow that actually works
Rules from the SigmaHQ community repository are written to catch real attacker behaviour but are not tuned for your specific environment. Running them without tuning produces noise that drowns out the signal. The process that works is to deploy the rule in alert mode for one week, review every single result manually, document what is legitimate, add those to the filter block, and only then move the rule to an automated alert.
# After running the schtasks rule for a week, you find three legitimate sources:
# 1. SCCM creating tasks during software deployment
# 2. A monitoring agent creating tasks on install
# 3. A backup tool creating a maintenance task
# Update the filter to exclude them:
filter_legit:
ParentImage|startswith:
- 'C:\Windows\System32\'
- 'C:\Windows\SysWOW64\'
- 'C:\Program Files\'
filter_sccm:
ParentImage|contains: 'CCM\CcmExec.exe'
filter_monitoring:
CommandLine|contains: 'MonitoringAgent'
condition: selection and not 1 of filter_*
Log coverage gaps that make rules silently fail
A rule can be syntactically correct, logically sound, and still return zero results because the underlying log source is not being collected. A Sigma rule for Sysmon Event ID 22 (DNS queries) returns nothing if Sysmon is not configured to log DNS queries. It does not error. It silently returns nothing, which looks identical to having no detections. Before deploying any rule, run a raw query against the data source it requires and confirm that events of that type actually exist in your SIEM. Zero results from the rule after confirming events exist means the rule logic needs work. Zero results because no events exist means your logging configuration has a gap.
Writing rules from scratch against real samples
The most valuable skill is writing a Sigma rule for a new technique or tool before a community rule exists. The process: observe the behaviour in a lab environment, identify which fields in which log source contain the distinguishing characteristics, write a detection block that matches those characteristics, write a filter block that excludes every legitimate process you can think of that produces similar output, and test against both malicious and clean samples.
# Lab-derived rule for a specific tool's behaviour
# Observation: Tool X always creates a named pipe with a specific prefix
# Log source: Sysmon Event ID 17 (PipeEvent - Pipe Created)
title: Suspicious Named Pipe Creation Pattern
logsource:
product: windows
category: pipe_created
detection:
selection:
PipeName|startswith:
- '\tool_x_prefix_'
- '\MSSE-' # Cobalt Strike default
- '\postex_' # Cobalt Strike post-exploitation
condition: selection
level: high