Writing Sigma Rules That Actually Fire
Most detection rules look fine on paper and never fire in production. The problem is rarely the rule itself; it is the rule's relationship to the log source it depends on. A short field guide to writing Sigma rules that survive contact with reality.
There is a particular failure mode in detection engineering: the rule that passes review, ships to production, and never produces a single alert. Sometimes that's because nothing it would alert on happens. More often, it's because the rule is structurally wrong in ways that don't surface until it meets real telemetry. The author wrote against an idea of the log; production is the actual log.
After enough cycles of this you start recognising the shapes. This post catalogues the four most common ones, all things we have seen go wrong with our own rules - and the discipline that makes them stop happening.
The wrong log source
Sigma rules declare a log source - Sysmon, Windows Security, EDR-specific telemetry. The conversion to a SIEM-specific query depends on this declaration. If the declared source isn't actually being collected on the hosts where the rule needs to fire, the rule is dead at deploy time.
This is the boring class of failure but easily the most common. Sysmon Event ID 1 (process create) gets used as a stand-in for any process telemetry, and on the 30% of fleet that doesn't run Sysmon the rule simply never matches. The detection engineer wrote it correctly; the deployment context is wrong.
Conditions that look specific but aren't
A common pattern in junior rules is matching on Image and CommandLine without realising that legitimate use of the same binary will trigger the same fields. The rule fires on every developer laptop, gets aggressively whitelisted, and then doesn't fire when it actually should because the whitelist is now too broad.
# Naive: matches every PowerShell -enc invocation, including legitimate ones
detection:
selection:
Image|endswith: '\powershell.exe'
CommandLine|contains: '-enc'
condition: selectionThe fix is to add the discriminating dimension that distinguishes attacker use from baseline. For PowerShell, that's usually parent process: an encoded command launched by Office is high-signal; the same launched by a developer's terminal is not. Adding ParentImage to the rule reduces false positives by an order of magnitude with one extra line.
detection:
selection:
Image|endswith: '\powershell.exe'
CommandLine|contains: '-enc'
parent:
ParentImage|endswith:
- '\winword.exe'
- '\excel.exe'
- '\outlook.exe'
condition: selection and parentField-name drift
Sigma normalises field names, but the normalisation isn't always perfect. CommandLine in Sysmon ID 1 is process_command_line in some EDR exports, and the CommandLine field in Windows Security 4688 is, depending on audit-policy configuration, sometimes empty. A rule targeting CommandLine on a 4688 source matches nothing if 'Include Command Line in Process Creation Events' is off - which is the Microsoft default.
The discipline is: never trust the field exists. Validate against actual production samples for every log source the rule targets. If the field is missing, the rule is wrong for that source - there is no equivalent of 'graceful degradation' in matching logic.
Technically correct, operationally useless
Less technical, more cultural. Detection rules need to fire into a process, not into a queue. A rule that fires 400 times a day in an environment where the SOC has bandwidth for 80 alerts is, for all operational purposes, not deployed. The first 80 events get triaged; the next 320 contribute to the death-by-a-thousand-cuts that destroys SOC morale.
If the rule cannot be tuned below a noise threshold the SOC can absorb, it does not belong in the production rule set. It belongs in a hunting library - a separate corpus run on demand against historical data, where the operating model is different. The detection engineer's job includes resisting the temptation to ship something that 'technically' detects; the operationalisation is part of the rule, not a separate problem.
Discipline that helps
- Validate every rule against real production samples before merging. If you can't find samples, the rule's threat model is theoretical and should be marked as such.
- Track per-rule signal/noise ratio for the first 30 days post-deploy. Any rule with a tuning ratio worse than 5:1 (false to true) goes back to the bench.
- Distinguish detection rules (run continuously, low FP) from hunting queries (run on demand, FP tolerated). Treat them as different deliverables.
- Annotate the threat-model assumption explicitly. Every rule has a hypothesis; surface it. 'Catches process injection from Office macros, assumes Sysmon ID 1 with parent fields.' That sentence is part of the rule.
None of this is novel. All of it is the kind of thing that gets left out of detection rule sets when the team is shipping fast. Detection engineering is an exercise in not skipping these steps.