Writing Sigma Rules That Actually Fire (Not Just Compile)
A detection engineer's guide to Sigma rules that survive production — pinned logsources, behavioral logic, field-mapping pipelines, tuned false positives, and CI.
Writing Sigma rules that actually fire is a different discipline from writing Sigma rules that compile. A rule that converts cleanly to SPL or KQL is a hypothesis, not a detection. Plenty of rule libraries are full of valid YAML that has never produced a single alert — because the logsource is not collected, the field names do not match the deployed pipeline, or the logic is so broad it gets tuned to death in week one. This guide is the checklist for Sigma that survives contact with production: what to verify before you write logic, how to structure the detection, how to convert it without silently breaking it, and how to prove it works before it merges.
What is a Sigma rule?
Sigma is a generic, YAML-based signature format for log events — the portable interchange standard for detection logic. You express a detection once in vendor-neutral terms, then compile it to whatever query language your platform speaks: Splunk SPL, Microsoft Sentinel KQL, Elastic (Lucene, EQL, or ES|QL), QRadar AQL, and others. The reference rule collection and specification live in the SigmaHQ repository.
Every rule has the same skeleton: metadata (title, id, status, level, ATT&CK tags), a
logsource block declaring which stream the rule applies to, a detection block holding one or
more named selections plus a condition that combines them, and a falsepositives list.
The status field carries more weight than most authors give it. experimental means untested
against production data, test means validated but not broadly deployed, and stable means
proven in production. Shipping everything as stable because it feels better destroys the one
signal your team has for knowing which rules to trust at 02:00.
Why do most Sigma rules never fire?
Because the rule is only one of four things that must all be true, and authors typically verify one of them. In practice, silent rules fail in a predictable order of frequency.
| Failure mode | What it looks like | How to confirm it |
|---|---|---|
| Logsource not collected | Rule valid, zero results, no errors | Query the raw index for any event of that category in the last 24h |
| Field-name mismatch | Query runs, matches nothing, no errors | Compare rule fields against an actual raw event’s keys |
| Wrong processing pipeline | Converts cleanly to the wrong field names | Print the converted query and read it before deploying |
| Value normalization | Field exists, values formatted differently | Check case, path separators, and whether the field is truncated |
| Over-broad logic | Fires constantly, disabled in week one | Look for the rule in your SIEM’s “muted” list |
| Condition inversion | filter written but never referenced | Read the condition line and confirm every selection is used |
The last one deserves special attention because it is invisible. A rule can define a perfectly
good filter block and then never reference it in condition, which means the exclusion silently
does nothing. Nothing errors, nothing warns, and the rule is noisier than its author believes.
What do you need before writing the rule?
Detection engineering has prerequisites, and skipping them is what produces libraries of rules that have never alerted. Confirm all four before writing logic.
1. The logsource is collected. Not “the agent is deployed” — the events are arriving in the index you will query, right now. Agent health and event delivery are different things.
2. The logsource is parsed into fields. Raw text arriving as a single blob is not queryable by field. Confirm that the events are structured and that the fields the rule needs actually exist on real events rather than in documentation.
3. Retention exceeds your detection window. A correlation rule looking back seven days against four days of retention silently under-matches. This is the failure that looks like a tuning problem and is actually a storage problem.
4. The volume is affordable. On Windows, Sysmon configuration for threat detection determines what is available to write rules against at all, and its tuning determines whether the SIEM bill survives the year. Enabling every event ID and writing rules afterwards is the expensive path.
For Windows process-creation rules specifically, decide up front whether you are targeting Security Event ID 4688 or Sysmon Event ID 1. They carry different fields — 4688 lacks the hashes and the parent command line that Sysmon provides — and a rule written for one will under-match against the other even though both are “process creation.”
How to write a Sigma rule that fires
Pin the logsource to data you actually collect
The logsource block is a claim about your environment, not a label. category: process_creation
with product: windows resolves through the processing pipeline to a concrete index and event ID.
Get this wrong and everything downstream is decorative.
Detect behaviour, not a single string
Brittle rules key on one literal — an exact path, a specific tool name — and break the moment an attacker renames a binary. Durable rules describe the behaviour: the parent/child process relationship, the command-line shape, the combination of fields that is unusual together.
title: Encoded PowerShell Command Execution
id: 9f3c1a7e-darkpwn-illustrative
status: experimental
description: >
Detects PowerShell invoked with an encoded command payload, a common wrapper
for obfuscated execution. Keys on the technique rather than a file hash so it
survives a renamed interpreter.
logsource:
category: process_creation
product: windows
detection:
selection_img:
- Image|endswith: '\powershell.exe'
- OriginalFileName: 'PowerShell.EXE'
selection_flags:
CommandLine|contains:
- ' -enc '
- ' -EncodedCommand '
- ' -e '
filter_known_deploy:
ParentImage|endswith: '\ccmexec.exe'
condition: selection_img and selection_flags and not filter_known_deploy
falsepositives:
- Software deployment tooling that wraps encoded payloads
- Some legitimate management and automation scripts
level: medium
tags:
- attack.execution
- attack.t1059.001 Two details make this rule durable rather than decorative. It matches OriginalFileName as well
as Image, so renaming powershell.exe to svchost.exe does not evade it — the PE metadata
still says PowerShell. And the filter_known_deploy block is actually referenced in condition,
which is the difference between an exclusion and a comment.
Convert with the right pipeline, then read the output
This is the step that silently breaks more rules than any other. Sigma’s field names are generic; your SIEM’s are not. A processing pipeline performs that translation.
sigma convert -t splunk -p sysmon -f savedsearches rules/encoded_powershell.yml
index=windows source="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" EventCode=1
(Image="*\\powershell.exe" OR OriginalFileName="PowerShell.EXE")
(CommandLine="* -enc *" OR CommandLine="* -EncodedCommand *" OR CommandLine="* -e *")
NOT (ParentImage="*\\ccmexec.exe") sigma convert -t kusto -p sysmon rules/encoded_powershell.yml
SecurityEvent
| where EventID == 1
| where (Image endswith @"\powershell.exe" or OriginalFileName == "PowerShell.EXE")
| where CommandLine has_any (" -enc ", " -EncodedCommand ", " -e ")
| where not (ParentImage endswith @"\ccmexec.exe") Drop the -p sysmon pipeline and the converter emits generic field names that do not exist in
either backend. The query is syntactically valid, executes without error, and returns nothing
forever. Always print the converted query and read it before it goes anywhere near production.
Which false positives will you actually see?
Every real detection has false positives. The rules that survive are the ones whose authors
enumerated them up front and provided a tuning path. A rule with an empty falsepositives block
has usually not met production yet.
| False positive | Why it happens | Resolution |
|---|---|---|
| Software deployment agents | SCCM/Intune wrap encoded payloads legitimately | Filter on the specific ParentImage, not on the flag |
| Vendor management tooling | RMM products invoke encoded commands by design | Allowlist the signed parent binary path |
| Admin automation scripts | Engineers genuinely use -enc for quoting | Scope the filter to named admin accounts on named hosts |
| Security scanners | Scanners replay attack patterns on purpose | Allowlist scanner source hosts explicitly |
| Build and CI agents | Pipelines invoke shells constantly | Exclude by service account plus host, never by account alone |
| EDR self-tests | Products generate their own test telemetry | Filter on the vendor’s known test signature |
| Backup and imaging jobs | Scheduled tasks spawn unusual parents at 03:00 | Time-plus-account scoped filter, reviewed quarterly |
Filtering on the service account alone is the mistake worth calling out. A build account excluded globally becomes the most attractive identity in the estate: anything running under it is invisible by policy. Scope every exclusion as narrowly as the data allows — account and host, or account and parent process — so that compromising the account does not also grant invisibility.
How do you tune a noisy rule without destroying it?
Tuning has a defensible order of operations. Work down it, and stop at the first step that fixes the problem:
- Narrow the logsource. If only one host class produces the noise, scope the rule rather than the exception.
- Add a scoped
filter. Account plus host, or parent plus path — never a single broad attribute. - Raise specificity in the selection. Add a second condition that the true positive satisfies and the benign case does not.
- Add a threshold via correlation. If a single occurrence is benign but ten in a minute is not, that is a correlation rule, not a tuning problem.
- Downgrade
level, do not delete. Alowrule that feeds a risk score still contributes; a deleted rule contributes nothing.
Set the starting threshold from your own baseline, not from a blog post. Measure the field’s distribution over 30 days of normal traffic, take the 99th percentile, and start just above it — then review after two weeks of real alerts. A threshold nobody derived is a threshold nobody can defend when it misses.
How do you test a Sigma rule before production?
A detection you have never seen fire is not validated. Testing has two halves, and skipping either one produces a rule that fails in the opposite direction.
True-positive validation. Generate the behaviour safely in an authorized lab. Atomic Red Team
publishes small, technique-mapped tests keyed to ATT&CK IDs, so a rule tagged attack.t1059.001
has a corresponding atomic to execute. Confirm the rule fires.
Benign validation. Run the converted query across a window of real production telemetry — seven to thirty days — and count the hits. This tells you the alert volume before an analyst experiences it, which is the number that decides whether the rule is deployable.
The recorded-sample corpus is the asset that makes this work. Capture the raw events from each atomic execution once, commit them as fixtures, and every future rule change is regression-tested against them for free. This is the mechanism behind the Sigma rule lifecycle: hypothesis to production, and it is what separates a rule library that compounds in value from one that rots.
Which backend should you target first?
If you run more than one platform, the honest answer is to author against the backend whose conversion fidelity you trust most and validate the others separately. Sigma is portable; it is not identical across targets.
| Consideration | Splunk (SPL) | Sentinel (KQL) | Elastic |
|---|---|---|---|
| Conversion maturity | Very mature | Very mature | Mature, multiple dialects |
| Field mapping burden | Pipeline-dependent, CIM helps | Table schema is opinionated | Depends heavily on ECS adoption |
| Case sensitivity | Case-sensitive by default | has/contains are case-insensitive | Analyzer-dependent |
| Correlation support | Native, mature | Native, mature | Varies by dialect |
| Biggest gotcha | Wildcard performance at scale | Table choice changes fields | Choosing the wrong query dialect |
The case-sensitivity row is the one that bites in cross-platform estates. The same Sigma rule can be strict on one backend and permissive on another, so a rule validated only in Sentinel may under-match in Splunk with no error to indicate it.
What about correlation rules?
Single-event rules cannot express “ten failures in a minute” or “this, then that, within an
hour.” The Sigma specification’s correlation extension covers exactly this: event_count for
volume thresholds, value_count for distinct-value thresholds such as one source touching many
accounts, and temporal for ordered sequences across several referenced rules.
Backend support is less uniform than for base rules, so verify that your target actually implements the correlation type you used rather than assuming it converted. This matters most for techniques where a single event is genuinely unremarkable — password spraying, enumeration, and staged execution chains that only look malicious in sequence.
Common Sigma rule mistakes
- Shipping
status: stableby default. It destroys the trust signal for everyone downstream. - Writing a
filterand forgetting it incondition. Silent, invisible, and common. - Excluding a service account globally. You just created the most attractive identity in the estate.
- Using
|containson a field your pipeline truncates. The substring is not there to match. - Tagging ATT&CK techniques aspirationally. A coverage map built from untested rules is fiction.
- Testing only the true positive. A rule that fires on the atomic and 40,000 times daily on production is not deployable.
- Never re-reading old rules. Environments change; a rule that fired last year may be silent now for reasons nobody noticed.
Map to ATT&CK and track coverage honestly
Tag rules with ATT&CK technique IDs and render the result on a coverage map — but count only validated rules. A technique covered by a rule that has never fired is not covered. Honest coverage beats an impressive-looking matrix, and it is the difference between the detection engineering workflow producing real assurance and producing a slide. When you map, resist claiming a technique from a rule that only catches one of its sub-techniques; record what you actually detect.
Sigma rule writing checklist
- Confirm the logsource is collected, parsed, and retained beyond the detection window.
- Read ten real events and note the exact field names and value formats.
- Write the detection on behaviour — parent/child, command-line shape, field combinations.
- Match
OriginalFileNamealongsideImagewhere the product supports it. - Enumerate
falsepositivesbefore shipping; leave none implied. - Scope every
filterto at least two attributes, and reference it incondition. - Convert with the production processing pipeline and read the emitted query.
- Fire the matching Atomic Red Team test and confirm the alert.
- Run the query over 7–30 days of benign telemetry and count the hits.
- Commit rule plus fixtures; require the CI gate to pass before merge.
- Set
statusandlevelhonestly, and tag ATT&CK only for what you validated. - Schedule a review date; a rule with no owner and no review is already decaying.
Where to build the skill (authorized labs)
Detection engineering is a hands-on craft, and the safest way to practice is in authorized labs that let you generate real attack telemetry on demand:
Disclosure: Some links below are affiliate links. If you buy through them, darkpwn may earn a commission at no extra cost to you. We only recommend training and tools we actually use in our own lab, and affiliate links never influence editorial coverage.
| TryHackMe | Hack The Box | |
|---|---|---|
| Best for | Guided blue-team paths | Open-ended adversary emulation |
| On-ramp | Gentle, browser-based | Steeper, more realistic |
| Detection focus | SOC / SIEM / Sigma rooms | Pro Labs + endpoint telemetry |
| Get started | TryHackMe | Hack The Box |
How do you know the rule is still firing six months later?
A rule that fired on the day it shipped is not a rule that works. Detection decays quietly: a log source changes format, an agent stops reporting, a field gets renamed upstream, and the rule keeps compiling and matching nothing. Nothing errors, so nobody notices until an incident review asks why it never fired.
Three checks catch effectively all of it. Alert on the absence of the log source, not just on the detection — if events from that source stop arriving, that is an incident in itself. Track per-rule fire counts over time, and treat a rule that has gone from regular hits to zero as broken until proven otherwise. And re-run the rule against fresh attack telemetry quarterly, because the validation that passed at authoring time says nothing about the current field schema.
The mindset that keeps a detection library honest: a rule is a claim about the world, and claims expire. Ownership, a review date, and a last-fired timestamp on every rule turn silent decay into something you can see on a dashboard.
The takeaway
The gap between a rule that compiles and a rule that fires is the entire job. Pin the logsource, detect behavior over strings, convert with the correct pipeline and read the output, write down the false positives, test against real attack telemetry and a benign baseline, and gate it all behind CI. Coverage you have not validated is a story you are telling yourself — fire the rule before the adversary does.
Put it into practice on real techniques: the behavioral Kerberoasting detection and the SQL injection detection guide both live or die by the rules-that-fire discipline above — as does detecting living-off-the-land binaries, which depends entirely on the Sysmon configuration for threat detection that feeds these rules. Scale it into a repeatable practice with the detection engineering workflow and the Sigma rule lifecycle: hypothesis to production, and take the same detection-as-code discipline to the endpoint with YARA rules for incident response. To build the hands-on reps, compare TryHackMe vs Hack The Box for security training. More in the Detection Engineering pillar.
Training & tools referenced
Disclosure: Some links below are affiliate links. If you buy through them, darkpwn may earn a commission at no extra cost to you. We only recommend training and tools we actually use in our own lab, and affiliate links never influence editorial coverage.
- TryHackMeGuided SOC and Sigma rooms to practice detection against real telemetrySecurity TrainingStart training
- Hack The BoxPro Labs and endpoint telemetry for open-ended detection practiceSecurity TrainingExplore labs
Frequently asked questions
What is Sigma in detection engineering?
Sigma is a generic, YAML-based signature format for log events. You write a detection once in Sigma, then convert it to your SIEM's query language (Splunk SPL, Microsoft KQL, Elastic, etc.) with a converter such as sigma-cli/pySigma. It is the de facto "write once, deploy anywhere" standard for detection-as-code.
Why do so many Sigma rules never fire?
The two most common reasons are a logsource that is not actually being collected (the rule is valid but there is no data behind it) and field names that do not match the deployed log pipeline. A rule that compiles is not a rule that fires — you have to test it against real telemetry.
How do you write a Sigma rule that actually fires?
Confirm the logsource is collected and parsed before writing logic, detect behaviour (parent/child relationships, command-line shape) rather than a single literal string, enumerate false positives in the falsepositives field up front, convert with the correct pySigma processing pipeline for your environment, and validate the rule against both attack telemetry and a benign baseline before merging.
What is a pySigma processing pipeline?
A processing pipeline tells the converter how to translate generic Sigma field names into the field names your SIEM actually stores. Sigma says Image and CommandLine; your index may store process_path and process_command_line. Without the right pipeline the query converts cleanly and matches nothing, which is the single most common cause of a silent rule.
How do you tune a noisy Sigma rule?
Add a scoped filter block that excludes the specific benign source — a named service account, a known deployment host, a signed parent process — rather than deleting the rule or broadening the condition. Tune by narrowing the exception, never by widening the detection, and record why each filter exists so it can be reviewed later.
How do you test a Sigma rule before deploying it?
Generate the behaviour safely in an authorized lab using Atomic Red Team for the mapped ATT&CK technique, confirm the rule fires on that true positive, then run it across a window of normal production telemetry to confirm the false-positive rate is acceptable. A rule that has never been seen to fire is a hypothesis, not a detection.
Is Sigma a replacement for your SIEM's native rules?
No. Sigma is a portable authoring and exchange format that compiles to native queries. Native rules still handle things Sigma deliberately does not model, such as vendor-specific correlation, risk scoring, and UEBA. Sigma's value is that detection logic becomes reviewable, version controlled, testable, and portable between backends.
What is a Sigma correlation rule?
Correlation rules are an extension of the Sigma specification that express logic across multiple events — event counts over a time window, distinct value counts, and temporal ordering of several rules. They cover detections that a single-event rule cannot, such as brute force by failure count or a sequence of stages within a fixed interval.