Sigma Rules: The Detection Engineer’s Rosetta Stone
Every security team has the same problem. Alerts pile up. Analysts burn out triaging noise. A new threat actor drops a technique, and the team scrambles to build a detection — only to realize it only works in Splunk, and half the org runs Elastic.
Sigma was built to solve this. Not just as a rule format, but as a philosophy: write your detection logic once, express it clearly, and let tooling handle the translation.
This post covers what Sigma is, why detection as code has become a foundational practice, and how to write rules that are precise, maintainable, and production-ready. Think of it as a reference you can return to every time you sit down to write a new detection.
What Is Sigma?
Sigma is an open, vendor-neutral rule format for describing detection logic. Think of it as YARA for log-based detections, or Snort rules for your SIEM.
A Sigma rule is a YAML file. It describes what you’re looking for in human-readable terms — a log source, a set of field conditions, and some metadata. It does not contain Splunk SPL, Elastic DSL, or any platform-specific query language. That translation step happens separately, via a tool called sigma-cli (or the older sigmac).
Here’s the core idea: one rule, many backends.
Write a Sigma rule for process creation. Convert it to Splunk. Convert it to Elastic. Convert it to Microsoft Sentinel KQL. The rule itself doesn’t change — only the output format does.
That portability is the whole game.
A Minimal Sigma Rule
title: Suspicious PowerShell Encoded Command
id: 7f3c2a1b-9d4e-4f8a-b0c1-2d3e4f5a6b7c
status: experimental
description: Detects PowerShell launched with an encoded command argument,
a common obfuscation technique used by attackers to hide malicious payloads.
logsource:
category: process_creation
product: windows
detection:
selection:
Image|endswith: '\powershell.exe'
CommandLine|contains:
- ' -EncodedCommand '
- ' -enc '
- ' -e '
condition: selection
falsepositives:
- Legitimate software deployment tools
- Some administrative scripts
level: medium
tags:
- attack.execution
- attack.t1059.001
That’s it. No query language. No platform config. Just logic.
Why Sigma Has Grown in Popularity
Sigma didn’t become the de facto detection standard overnight. It earned that position by solving real problems that detection teams face every day.
1. The Multi-SIEM Reality
Most organizations don’t run a single SIEM forever. They migrate. They acquire companies with different stacks. They run a hybrid — Elastic for endpoint, Splunk for network, Sentinel for cloud. Writing native queries means writing the same detection three times and maintaining three versions when the technique evolves.
Sigma collapses that into one source of truth.
2. Community-Driven Coverage
The SigmaHQ GitHub repository contains thousands of community-contributed rules, mapped to MITRE ATT&CK. When a new technique surfaces in the wild, rules often appear within days. That’s a force multiplier no single team can replicate internally.
3. Human Readability
Sigma rules read like English — or close enough. A junior analyst can look at a rule and understand what it’s detecting. A threat intelligence analyst can write a rule without knowing Splunk SPL. That lowers the barrier to contribution across the whole team.
4. Tool Ecosystem Maturity
sigma-cli, Uncoder.io, Detection-as-Code pipelines, SIEM vendor integrations — the tooling around Sigma has matured significantly. Converting a rule to your platform’s query language is now a one-line command or a CI/CD step.
Detection as Code: Why It Matters
Detection as code means treating your detection rules the same way software engineers treat source code: version-controlled, peer-reviewed, tested, and deployed through an automated pipeline.
This is a shift in mindset. And it’s an important one.
The Old Way
An analyst spots a technique. They write a SIEM query in the UI. They tweak it until it doesn’t fire too often. They save it. That query lives in the SIEM, undocumented, unreviewed, and forgotten — until it breaks or floods the queue.
The Detection-as-Code Way
An analyst spots a technique. They write a Sigma rule in a text editor. They open a pull request. A teammate reviews the logic and the false positive list. An automated pipeline converts the rule, validates the syntax, runs test cases against sample log data, and deploys to the SIEM on merge.
The detection is now versioned. It has an author. It has a changelog. It can be rolled back. It can be tested before it ever touches production.
Why This Model Wins
Accountability. Every rule has an owner and a history. When a rule causes alert fatigue, you can see who wrote it, what they were thinking, and what changed.
Velocity. Automation eliminates the manual steps between “we need this detection” and “this detection is live.” Teams that ship detections faster respond to threats faster.
Quality. Peer review catches logic errors, missing false positive handling, and threshold problems before they hit production analysts.
Portability. Git-stored Sigma rules move with your team. If you change SIEMs, your detections go with you.
Anatomy of a Sigma Rule: Field-by-Field Reference
Let’s break down every component of a production-quality Sigma rule.
---
title: "Short, descriptive title — what is this detecting?"
id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx # UUID — generate one per rule
status: experimental | test | stable
description: >
A clear explanation of what this rule detects and why it matters.
Include the attacker behavior, not just the observable.
references:
- https://attack.mitre.org/techniques/TXXX/
- https://any-blog-or-advisory-that-explains-the-technique.com
author: "Your Name or Handle"
date: YYYY-MM-DD
modified: YYYY-MM-DD
tags:
- attack.tactic_name # e.g. attack.execution
- attack.tXXX.XXX # specific ATT&CK technique ID
logsource:
category: process_creation # standardized log category
product: windows # or linux, azure, aws, etc.
detection:
selection:
FieldName|modifier: value
filter:
FieldName: excluded_value
condition: selection and not filter
falsepositives:
- List known benign scenarios that will trigger this rule
level: informational | low | medium | high | critical
---
Title and ID
The title should describe the behavior, not the tool. Mimikatz Execution is weaker than LSASS Memory Access by Non-System Process. The behavior is detectable in perpetuity. The tool name is not.
The id field is a UUID. Generate it once per rule and never change it. It’s how downstream tooling tracks rules across versions and renames.
Status
- experimental — New rule, not yet validated in a real environment. Expect tuning needed.
- test — Validated logic, being evaluated for noise level.
- stable — Production-ready. False positive rate is understood and documented.
Ship rules as experimental. Promote them as they prove out.
Log Source
This is how sigma-cli knows which index or data source to query. Common categories include:
| Category | What it covers |
|---|---|
process_creation |
New process start events (Sysmon Event ID 1, Windows Security 4688) |
network_connection |
Outbound network connections (Sysmon Event ID 3) |
file_creation |
New files written to disk (Sysmon Event ID 11) |
registry_set |
Registry key writes (Sysmon Event ID 13) |
authentication |
Login events, MFA, SSO |
cloud_trail |
AWS CloudTrail API calls |
dns_query |
DNS resolution events |
Detection Block
This is the heart of the rule. It uses a structured condition language.
Field modifiers change how values are matched:
| Modifier | Behavior |
|---|---|
contains |
Substring match anywhere in the field value |
startswith |
Match from the beginning of the field value |
endswith |
Match at the end of the field value |
re |
Full regular expression match |
gt / lt / gte / lte |
Numeric comparisons |
cidr |
CIDR range match for IP addresses |
all |
ALL listed values must match (AND within a list) |
Lists are OR by default. If you list three values under a field, the rule matches if any one of them is present. Use |all when you need all values to be present simultaneously.
detection:
# This matches if CommandLine contains -enc OR -EncodedCommand
selection_or:
CommandLine|contains:
- '-enc'
- '-EncodedCommand'
# This matches only if BOTH strings are present
selection_and:
CommandLine|contains|all:
- '-enc'
- 'powershell'
Condition logic combines named blocks:
condition: selection and not filter
condition: selection_a or selection_b
condition: (selection_a or selection_b) and not filter
condition: selection | count() by TargetUsername > 5
That last one is an aggregation condition — useful for detecting repeated failures, high-volume enumeration, or MFA fatigue patterns.
False Positives
Be specific here. Legitimate admin activity is useless. Backup software X accessing LSASS for credential vault sync is useful. Specific false positive documentation speeds up triage dramatically.
Level
| Level | When to use it |
|---|---|
informational |
Context only, not actionable alone |
low |
Interesting but high false positive rate |
medium |
Warrants investigation when combined with other signals |
high |
Strong signal, investigate promptly |
critical |
Near-certain malicious activity, respond immediately |
Be honest. Inflating levels causes alert fatigue. A critical rule that fires on benign activity every day trains analysts to ignore critical.
Writing for Precision and Recall
Every detection involves a tradeoff between precision (how often the alert is real) and recall (how many real attacks you catch).
A rule that catches everything will cry wolf constantly. A rule that only fires on exact attacker tooling will miss novel variations.
The goal is to find the behavior that is:
- Reliably present in the attacker’s technique
- Reliably absent in normal operations
Start Broad, Then Narrow
Write your first draft to maximize recall. Catch the technique in its broadest form. Then look at what legitimate activity would also match. Add filters — by path, by parent process, by user, by time of day — until the false positive rate is acceptable.
Don’t start narrow. Starting narrow makes it easy to miss the thing you’re trying to catch.
Use Parent Process Context
detection:
selection:
Image|endswith: '\cmd.exe'
ParentImage|endswith:
- '\winword.exe'
- '\excel.exe'
- '\outlook.exe'
condition: selection
cmd.exe alone is useless noise. cmd.exe spawned by Word, Excel, or Outlook is a near-certain macro execution. Parent process is one of the highest-value context fields in process creation logs.
Compound Conditions Beat Single Conditions
One observable is noise. Two correlated observables are a signal. Three are a case.
If your rule has a high false positive rate on its own, consider whether it belongs in a suite where correlated firing elevates confidence — rather than trying to make it standalone.
Testing Your Rules
A rule without a test case is a guess. You don’t know if it fires. You don’t know if the field names match your environment. You don’t know what it misses.
What Good Test Cases Look Like
Every rule should have at least two test cases:
- A true positive — a log sample that represents the attacker behavior. The rule must fire.
- A true negative — a log sample that represents the benign equivalent. The rule must not fire.
For a rule detecting cmd.exe spawned by Office apps, your true negative is cmd.exe spawned by explorer.exe (normal user activity). Your true positive is cmd.exe spawned by winword.exe.
sigma-cli and evtx-sigma
The sigma-cli toolchain supports test execution against sample log data. Pair it with tools like evtx-sigma to run rules against real EVTX log samples captured from test environments.
For cloud detections, capture sanitized real log samples from your environment and version-control them alongside your rules.
Atomic Red Team
Atomic Red Team provides small, targeted test scripts mapped to ATT&CK techniques. Run atomics in a lab environment, capture the resulting logs, and validate your rule fires. This closes the loop between writing a rule and knowing it works.
CI/CD for Detection Rules
A detection-as-code pipeline automates the path from rule authorship to production deployment. Here’s a practical pipeline structure:
Pull Request Opened
│
▼
Lint & Validate ← sigma-cli validate; check YAML syntax, required fields
│
▼
Unit Tests ← run rules against true positive / true negative log samples
│
▼
Peer Review ← human review of logic, false positive list, severity level
│
▼
Merge to Main
│
▼
Convert to Backend ← sigma-cli convert -t splunk / elastic / sentinel / etc.
│
▼
Deploy to SIEM ← push via API, Terraform, or SIEM-native pipeline
│
▼
Monitor Alert Rate ← track fire rate post-deployment; flag runaway rules
Key Pipeline Steps in Practice
Validation catches structural errors before review. Run sigma-cli check on every changed .yml file. Fail the pipeline if required fields (title, id, status, logsource, detection, level) are missing.
Conversion should be backend-specific and version-pinned. If you update your Sigma version, review conversion output before deploying. Backend translation logic can change between versions.
Deployment can use your SIEM’s API. Splunk has a REST API for saved searches. Elastic has detection rules APIs. Sentinel supports ARM template deployment. Build the deployment step once and reuse it across all rules.
Rollback should be one command. If a rule causes runaway alerts post-deployment, you need to disable or remove it immediately. Treat it like a bad code deploy.
Performance Metrics: How Do You Know If Your Detections Work?
Writing rules is the start. Measuring them is how you improve.
The Four Metrics That Matter
True Positive Rate (TPR / Recall) Of all the real attacks that occurred, what percentage did your rules fire on? This is hard to measure without red team exercises or breach simulations, but it’s the most important number.
False Positive Rate (FPR / Precision) Of all the alerts your rules generated, what percentage were real? Track this per rule. A rule with a 5% true positive rate is not a detection — it’s scheduled noise.
Mean Time to Detect (MTTD) From the moment an attack technique executes to the moment an alert fires. A correct but slow detection has limited value. Aim for near-real-time on high-severity rules.
Alert Volume per Rule Track how many times each rule fires per day or per week. A rule that fires 500 times a day deserves scrutiny. Either it’s catching a lot of real activity (great) or it’s generating noise (investigate). Sort your rule library by fire rate regularly.
Building a Detection Health Dashboard
Track these fields per rule in a simple spreadsheet or detection catalog:
| Field | Description |
|---|---|
| Rule ID | Sigma UUID |
| Technique | ATT&CK ID |
| Status | experimental / test / stable |
| Fire rate (7d) | How often it’s alerting |
| TP rate | Estimated based on analyst review |
| Last reviewed | Date of last false positive audit |
| Owner | Who maintains this rule |
Review the catalog quarterly. Retire rules that never fire and haven’t been triggered by red team exercises. They may be correct in theory but irrelevant to your actual environment.
Quick Reference: Common Mistakes
Using tool names instead of behaviors.
Tools change. Behaviors don’t. Detect LSASS access with VM_READ permissions, not Mimikatz.exe.
Skipping the filter block. Every rule needs to account for benign activity that matches the same pattern. Write the filter block even if it’s short.
Setting everything to high or critical.
This destroys the signal-to-noise ratio for your analysts. Level is a contract. Honor it.
Not pinning your Sigma version in CI/CD. Backend translation output can change between Sigma releases. Pin versions and review output on upgrades.
Writing rules without testing them. If you haven’t seen it fire on a true positive, you don’t know if it works.
Treating rules as set-and-forget. Environments change. New software gets deployed. Old attacker tooling gets retired. Review your rule library on a regular cadence.
Where to Go From Here
Sigma’s ecosystem is rich and growing. Here are the essential resources:
- SigmaHQ GitHub — The canonical rule repository and
sigma-clitool - MITRE ATT&CK — Map every rule to a technique; use the framework to find gaps in your coverage
- Atomic Red Team — Technique-level test scripts to validate your rules fire
- Uncoder.io — Browser-based Sigma converter; useful for quick translation without a full pipeline
- Detection Engineering Weekly — Newsletter covering the detection engineering discipline
Detection engineering is a practice, not a project. The teams that get good at it treat every new threat advisory as an opportunity to build coverage — not just for today’s attacker, but for the next one who uses the same playbook.
Write the rule. Test it. Ship it. Measure it. Repeat.
Have a detection engineering tool or resource that’s changed how your team works? This post will be updated as the ecosystem evolves. Drop a comment below.