Detection Engineering

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.

Detection-as-code pipeline visualization showing Sigma rules flowing into a SIEM
Threat reference

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 modeWhat it looks likeHow to confirm it
Logsource not collectedRule valid, zero results, no errorsQuery the raw index for any event of that category in the last 24h
Field-name mismatchQuery runs, matches nothing, no errorsCompare rule fields against an actual raw event’s keys
Wrong processing pipelineConverts cleanly to the wrong field namesPrint the converted query and read it before deploying
Value normalizationField exists, values formatted differentlyCheck case, path separators, and whether the field is truncated
Over-broad logicFires constantly, disabled in week oneLook for the rule in your SIEM’s “muted” list
Condition inversionfilter written but never referencedRead 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.

Sigma Encoded PowerShell via -EncodedCommand source
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.

SPL The Same Detection After Conversion to Splunk
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")
KQL The Same Detection After Conversion to Microsoft Sentinel
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 positiveWhy it happensResolution
Software deployment agentsSCCM/Intune wrap encoded payloads legitimatelyFilter on the specific ParentImage, not on the flag
Vendor management toolingRMM products invoke encoded commands by designAllowlist the signed parent binary path
Admin automation scriptsEngineers genuinely use -enc for quotingScope the filter to named admin accounts on named hosts
Security scannersScanners replay attack patterns on purposeAllowlist scanner source hosts explicitly
Build and CI agentsPipelines invoke shells constantlyExclude by service account plus host, never by account alone
EDR self-testsProducts generate their own test telemetryFilter on the vendor’s known test signature
Backup and imaging jobsScheduled tasks spawn unusual parents at 03:00Time-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:

  1. Narrow the logsource. If only one host class produces the noise, scope the rule rather than the exception.
  2. Add a scoped filter. Account plus host, or parent plus path — never a single broad attribute.
  3. Raise specificity in the selection. Add a second condition that the true positive satisfies and the benign case does not.
  4. 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.
  5. Downgrade level, do not delete. A low rule 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.

ConsiderationSplunk (SPL)Sentinel (KQL)Elastic
Conversion maturityVery matureVery matureMature, multiple dialects
Field mapping burdenPipeline-dependent, CIM helpsTable schema is opinionatedDepends heavily on ECS adoption
Case sensitivityCase-sensitive by defaulthas/contains are case-insensitiveAnalyzer-dependent
Correlation supportNative, matureNative, matureVaries by dialect
Biggest gotchaWildcard performance at scaleTable choice changes fieldsChoosing 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: stable by default. It destroys the trust signal for everyone downstream.
  • Writing a filter and forgetting it in condition. Silent, invisible, and common.
  • Excluding a service account globally. You just created the most attractive identity in the estate.
  • Using |contains on 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

  1. Confirm the logsource is collected, parsed, and retained beyond the detection window.
  2. Read ten real events and note the exact field names and value formats.
  3. Write the detection on behaviour — parent/child, command-line shape, field combinations.
  4. Match OriginalFileName alongside Image where the product supports it.
  5. Enumerate falsepositives before shipping; leave none implied.
  6. Scope every filter to at least two attributes, and reference it in condition.
  7. Convert with the production processing pipeline and read the emitted query.
  8. Fire the matching Atomic Red Team test and confirm the alert.
  9. Run the query over 7–30 days of benign telemetry and count the hits.
  10. Commit rule plus fixtures; require the CI gate to pass before merge.
  11. Set status and level honestly, and tag ATT&CK only for what you validated.
  12. 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.

Two authorized training platforms commonly used to practice detection against real attack telemetry.
TryHackMeHack The Box
Best for Guided blue-team pathsOpen-ended adversary emulation
On-ramp Gentle, browser-basedSteeper, more realistic
Detection focus SOC / SIEM / Sigma roomsPro 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 Training
    Start training
  • Hack The BoxPro Labs and endpoint telemetry for open-ended detection practiceSecurity Training
    Explore 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.