{"id":184,"date":"2024-01-09T09:00:00","date_gmt":"2024-01-09T09:00:00","guid":{"rendered":"http:\/\/justruss.tech\/index.php\/2024\/01\/09\/writing-sigma-rules-that-actually-work\/"},"modified":"2026-05-13T12:59:35","modified_gmt":"2026-05-13T12:59:35","slug":"writing-sigma-rules-that-actually-work","status":"publish","type":"post","link":"https:\/\/justruss.tech\/index.php\/2024\/01\/09\/writing-sigma-rules-that-actually-work\/","title":{"rendered":"Writing Sigma Rules That Actually Work"},"content":{"rendered":"<p>Sigma is a vendor-neutral detection rule format. A single Sigma rule can be converted to Splunk SPL, Elastic EQL, Microsoft Sentinel KQL, QRadar AQL, and others using the <code>sigma-cli<\/code> or the older <code>sigmac<\/code> tool. The value is<br \/>\nwrite-once, deploy-anywhere \u2014 but only if the rules are properly tuned for your environment first.<\/p>\n<h3>Sigma rule structure<\/h3>\n<pre>title: Suspicious Scheduled Task Creation\nid: 92a5a95a-cce6-4b88-a5d0-c578d0d20d7e\nstatus: stable\ndescription: Detects scheduled task creation from unusual parent processes\nreferences:\n    - https:\/\/attack.mitre.org\/techniques\/T1053\/005\/\nauthor: justruss\ndate: 2024\/01\/09\ntags:\n    - attack.persistence\n    - attack.t1053.005\nlogsource:\n    product: windows\n    category: process_creation\ndetection:\n    selection:\n        Image|endswith: \\schtasks.exe\n        CommandLine|contains: \/create\n    filter_legit:\n        ParentImage|startswith:\n            - C:\\Windows\\System32\\\n            - C:\\Windows\\SysWOW64\\\n            - C:\\Program Files\\\n    condition: selection and not filter_legit\nfalsepositives:\n    - Software installers\n    - Admin scripts\nlevel: medium<\/pre>\n<h3>Converting with sigma-cli<\/h3>\n<pre># Install\npip install sigma-cli\npip install pysigma-backend-splunk pysigma-backend-elasticsearch\n\n# Convert to Splunk SPL\nsigma convert -t splunk -p splunk_windows rule.yml\n\n# Output:\n# (Image=\"*\\schtasks.exe\" CommandLine=\"*\/create*\")\n# NOT (ParentImage=\"C:\\Windows\\System32\\*\"\n#   OR ParentImage=\"C:\\Windows\\SysWOW64\\*\"\n#   OR ParentImage=\"C:\\Program Files\\*\")\n\n# Convert to Elastic EQL\nsigma convert -t elasticsearch -p ecs_windows rule.yml<\/pre>\n<h3>The noise problem in practice<\/h3>\n<p>Running the raw schtasks rule in a test environment with 50 Windows endpoints over one week produced 847 matches. Breaking down by ParentImage:<\/p>\n<pre>ParentImage                               Count\n-----------------------------------------  -----\nC:\\Windows\\System32\\cmd.exe               312\nC:\\Windows\\System32\\WindowsPowerShell\\... 289\nC:\\Program Files\\ManageEngine\\...         156\nC:\\Users\\admin\\AppData\\Local\\Temp\\...      67  &lt;-- worth investigating\nC:\\Windows\\Temp\\install.exe                23  &lt;-- worth investigating<\/pre>\n<p>The ManageEngine entries are legitimate IT management software creating maintenance tasks. Adding them to the filter drops 156 false positives immediately. The Temp directory entries are worth investigating \u2014 legitimate installers sometimes run<br \/>\nfrom Temp but it is also a common attacker staging location.<\/p>\n<h3>Building environment-specific filters<\/h3>\n<p>The exclusion list should be maintained as a separate lookup file rather than hardcoded in the rule, so it can be updated without touching the detection logic:<\/p>\n<pre># legitimate_schedulers.csv\nParentImage,Note\nC:\\Program Files\\ManageEngine\\UEMS_Agent\\bin\\dcagentservice.exe,ManageEngine UEM\nC:\\Program Files (x86)\\Symantec\\Symantec Endpoint Protection\\smc.exe,Symantec SEP\nC:\\Windows\\System32\\services.exe,SCM scheduled task registration<\/pre>\n<p>In Splunk, join against this lookup at query time rather than hardcoding exclusions in the SPL. This keeps the detection rule generic and the environment-specific tuning in a maintainable separate file.<\/p>\n<h3>Detection maturity tiers<\/h3>\n<p>Not all Sigma rules are equal. The SigmaHQ repository tags rules by status: stable, test, experimental. Running experimental rules in production without tuning will generate substantial noise. A practical approach:<\/p>\n<ul>\n<li>Start with <code>status: stable<\/code> and <code>level: high<\/code> rules only<\/li>\n<li>Run each for one week and review all matches before moving to alerting<\/li>\n<li>Document every exclusion added with a date and reason<\/li>\n<li>Revisit exclusions quarterly \u2014 software changes, exclusions become stale<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>A week of trialling Sigma rules against a real lab environment. A lot of<br \/>\npublic rules are too noisy to be useful without tuning.<\/p>\n","protected":false},"author":0,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[13],"tags":[],"class_list":["post-184","post","type-post","status-publish","format-standard","hentry","category-threat-hunting"],"_links":{"self":[{"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts\/184","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/comments?post=184"}],"version-history":[{"count":2,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts\/184\/revisions"}],"predecessor-version":[{"id":226,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts\/184\/revisions\/226"}],"wp:attachment":[{"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/media?parent=184"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/categories?post=184"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/tags?post=184"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}