Sigma Rules.
One YAML, Any Backend.
A static reference for Sigma syntax and the judgment calls that go with it: what each field of a rule does, how detection blocks and conditions combine, and the habits that keep a rule portable across SIEMs instead of silently matching nothing. No conversion engine here, no live testing, just the reference. For the Windows Event IDs a Sigma rule's EventID field actually matches against, see the Windows Event ID Cheatsheet.
Rule Anatomy
Eight fields, most of them plain metadata. The two that actually decide a match are logsource and detection.
The Fields, In Order
title: A short, specific sentence describing what fired └── Shows up in SIEM alert lists, so it has to mean something at 2am without opening the rule // "Suspicious Activity" tells the analyst nothing, a good title tells them what to check first id: A UUID, unique to this rule, never reused └── How dedup, rule updates, and cross-references to this exact rule work // changing the id turns an update into a brand new rule as far as tooling is concerned status: experimental | test | stable | deprecated | unsupported └── Signals how much you trust the rule, not whether it's syntactically valid // most rules ship as experimental and get promoted after they survive real traffic logsource: category / product / service ├── Tells the backend which log source this rule targets, e.g. process_creation on windows └── The wrong combination here means the rule compiles fine and matches nothing, ever // this is the field most pitfalls in this guide trace back to detection: required selection blocks + condition ├── One or more named blocks of field: value pairs, plus a condition that combines them └── The only section that defines actual matching logic // everything above this line is context, everything here is the rule itself level: informational | low | medium | high | critical └── Severity as the rule author sees it, feeds triage priority downstream // level is a judgment call, not a technical property of the query tags: attack.execution, attack.t1059.001, ... └── Maps the rule to ATT&CK tactics and techniques for coverage tracking // a rule with no ATT&CK tag is invisible to any heat-map built from your ruleset falsepositives: A list of legitimate activity that can trigger this rule └── Sets the analyst's expectation before the first alert ever fires // "Unknown" is a valid answer only after you've actually looked, not a default
Detection Block Syntax
Selection blocks are field: value maps. Modifiers change how a value matches, list values change what counts as a match.
Field Matching
field: value pair is an exact match against the field's normalized value. Every field inside a selection block is combined with AND by default, so Image: powershell.exe and CommandLine: -enc in the same block both have to hold for that block to match.Value Modifiers
contains— value appears anywhere in the fieldstartswith/endswith— value anchors to one end of the fieldre— value is a regular expression, not a literalcased— forces case-sensitive comparison, off by defaultall— every value in a list must match, not just onebase64offset— matches the value against all three base64 byte-alignmentswindash— matches command-line switches across-,/, and en-dash variants
List Values Are OR
CommandLine|contains: ['-enc', '-EncodedCommand'] matches if either string appears. Add the all modifier if the intent is actually "every value in this list must be present."Condition Logic
condition: wires the named selection blocks together. Correlation rules extend that logic across multiple base rules over time.
Selection And Not Filter
selection and the exclusion as a separate filter block, then write condition: selection and not filter. The two blocks stay independently readable instead of one tangled negated expression.Wildcard Block References
1 of selection*— at least one block whose name starts withselectionmatchedall of filter*— every block whose name starts withfiltermatchedall of them— every selection block defined in detection: matched
Correlation Rules
event_count for how many times a rule fired, value_count for how many distinct values of a field appeared, or temporal to require several rules to fire within a shared window. It turns "this fired once" into "this fired five times from the same host in ten minutes."Best Practices
The difference between a rule that ports cleanly to any backend and one that only ever worked on the SIEM it was written against.
Category And Product Together
category and product under logsource, for example process_creation plus windows, instead of product alone. Most backends map category to a specific table or index; without it, the converted query either targets the wrong source or nothing at all.Filter Blocks Over Inline Negation
filter selection and reference it as not filter in the condition, rather than piling negated field checks into the main selection block. It keeps the positive match and the exclusion list independently editable.Tag Every ATT&CK Technique
attack.t1059.001, not just the tactic. A rule tagged only attack.execution tells a coverage matrix almost nothing about which sub-techniques your detections actually reach.Falsepositives, Not A Formality
Common Pitfalls
Small mistakes that pass a syntax check and only show up once the rule is deployed against real traffic.
Assuming Case Sensitivity
PowerShell.exe still matches powershell.exe in the log. Add |cased to the field if the rule genuinely needs an exact-case comparison, otherwise the default behavior is doing more matching than the YAML implies.Trusting The YAML, Not The Conversion
Silent Logsource Mismatch
logsource.product or category that doesn't match what the backend expects fails with zero matches and no error. The rule parses, converts, and deploys cleanly, it just never sees the log source it was meant to. Verify the logsource mapping against the target backend's field mapping config before assuming a quiet rule is a clean rule.Full Example
Every field from the sections above, in one rule, doing real work.
Suspicious PowerShell EncodedCommand Execution
title: Suspicious PowerShell EncodedCommand Execution id: 5f2e7f3a-3c1e-4b8a-9f2d-1a2b3c4d5e6f status: experimental logsource: category: process_creation product: windows detection: selection: Image|endswith: '\powershell.exe' CommandLine|contains: - '-enc' - '-EncodedCommand' condition: selection level: medium tags: - attack.execution - attack.t1059.001 falsepositives: - Legitimate admin scripts // Image|endswith anchors on the binary regardless of install path, CommandLine|contains // treats the two encoded-command flags as an OR since they're a list under one field. // category + product together is what lets this convert cleanly to more than one backend.