Automated SigmaHQ to Wazuh
modular XML rule conversion

A dynamic CLI pipeline that scans the Windows SigmaHQ rules tree, discovers its logsource categories and services, and generates modular Wazuh XML files—with persistent Rule IDs and validation before deployment.

By Ashraf GamalUpdated 3 November 2026Wazuh 4.14.0 · Sigma · PythonApprox. 9 min read
Sigma to Wazuh XML pipeline showing Sigma YAML rules flowing through the sigma2wazuh.py Python converter into modular Wazuh XML rule files.
Sigma to Wazuh XML Pipeline — from Sigma YAML source rules, through Python conversion, to modular Wazuh XML files.

Field note: This article documents a focused conversion workflow and a Python prototype for a Wazuh 4.14.0 lab. It is not an official SigmaHQ or Wazuh backend. Conversion is a starting point for detection engineering—not proof that every Sigma rule is semantically equivalent after translation. Review and test each rule before enabling it in production.

1. Objective and scope

SigmaHQ provides vendor-neutral detection rules in YAML. Wazuh evaluates native XML rules against decoded event fields. A useful conversion workflow therefore needs more than a text substitution: it must map fields, preserve selection logic where supported, chain rules to the correct Wazuh parent rule, and assign IDs that remain stable across conversion runs.

The converter now scans the complete sigma/rules/windows tree by default. It discovers each rule's logsource.category or logsource.service, normalizes related values into an output bucket, and writes one file per discovered bucket. It does not rely on a three-category allowlist.

Logsource examplesOutput bucketOutput file
ps_script, ps_module, classic PowerShell servicesPowerShellsigma_powershell.xml
registry_event, registry_add, registry_delete, registry_set, registry_renameRegistrysigma_registry.xml
network_connection, create_remote_thread, pipe_created, wmi_eventSeparate per-category filessigma_network_connection.xml, etc.
Other discovered Windows categories and servicesSeparate per-category filessigma_<bucket>.xml

The only category folders intentionally excluded by the default run are process_creation and file_event. The terminal output you shared shows roughly 45–49 category XML files; the exact number is determined dynamically by the SigmaHQ checkout and its available logsource values. Rules with unsupported fields, modifiers, or condition syntax are still skipped and counted for manual review.

2. Conversion design and constraints

Custom Rule ID allocation

Wazuh recommends using IDs in the 100000–120000 range for custom rules. This workflow starts at 113000 and stores each Sigma rule UUID-to-Wazuh-ID assignment in sigma_rule_ids.json. Keep that file with the converter output and back it up: it preserves IDs when you rerun the full Windows tree or update the SigmaHQ checkout.

Parent-rule mapping

Child rules should be chained to the built-in Wazuh rule that has already recognized the relevant event type. The converter includes specific mappings for common Sysmon sources—for example, Event ID 3 uses parent SID 61605, registry Event IDs 12, 13 and 14 use 61614, 61615 and 61616, and the pipe/WMI buckets add Event ID guards under parent 61600.

Standard Windows channels also have separate parent rules: Security 60001, System 60002, Application 60003, Sysmon 60004, Defender 60005, and Windows Firewall 60016. PowerShell rules use the generic EventChannel parent 60000 with an additional channel filter for Microsoft-Windows-PowerShell/Operational or Windows PowerShell. Confirm the channel string and parent hierarchy against your local 4.14.0 ruleset and actual events before deployment.

For other Windows services that do not have a specific built-in mapping, the default fallback is 60000. The script marks these rules in its console summary. A generic parent is intentionally less selective: inspect the Sigma fields and add a verified mapping with --parent-sid CATEGORY=SID, or use --no-fallback-parent to skip categories that lack a specific mapping. Never treat a generated rule as validated solely because the XML parses.

Boolean logic and value modifiers

In a Wazuh rule, multiple field conditions are cumulative. A Sigma list of values normally means “match any value”, while the Sigma all modifier means every value must match. The converter handles supported cases by combining alternative values into a PCRE2 alternation or emitting separate conditions for all. Simple and, or, 1 of, and all of selection patterns are supported; more complex expressions are skipped for manual translation.

The included prototype supports a limited set of string modifiers, including contains, startswith, endswith, windash, re, and cased. It does not claim full Sigma specification coverage.

3. Prerequisites

Confirm that your installed decoder actually exposes the field paths used by the converter. The mappings in a Sigma rule and the fields produced by your Wazuh decoders are not automatically interchangeable.

4. Prepare the build workstation

sudo apt update
sudo apt install -y git python3 python3-yaml libxml2-utils

mkdir -p ~/sigma-wazuh-custom
cd ~/sigma-wazuh-custom

git clone --depth 1 https://github.com/SigmaHQ/sigma.git

Download the focused converter from the portfolio repository and make it executable:

cd ~/sigma-wazuh-custom
curl -fL \
  https://agamalx0.github.io/downloads/sigma2wazuh.py \
  -o sigma2wazuh.py
chmod +x sigma2wazuh.py
python3 -m py_compile sigma2wazuh.py

Download the sigma2wazuh.py converter. The script discovers all Windows logsource buckets by default and keeps unsupported rule logic fail-closed: an unmapped Sigma field or unsupported condition is skipped instead of being converted into a generic full-event text match. The optional --include argument remains available when you deliberately want a smaller test run.

5. Run the all-category conversion

Run the converter against the full Windows rules directory. No --include argument is needed: omitting it enables dynamic discovery of every category/service bucket found in this SigmaHQ checkout. The two default exclusions are explicit in the command so the scope is visible.

mkdir -p "$HOME/wazuh-existing-rules"
# Copy existing custom XML rules into this directory before allocating IDs.

./sigma2wazuh.py \
  --input "$HOME/sigma-wazuh-custom/sigma/rules/windows" \
  --output-dir "$HOME/sigma-wazuh-custom/output_xml" \
  --exclude process_creation file_event \
  --start-id 113000 \
  --group sigma_wazuh \
  --prefix "SN - " \
  --fallback-parent-sid 60000 \
  --existing-xml "$HOME/sigma-wazuh-custom/output_xml/*.xml" \
                "$HOME/wazuh-existing-rules/*.xml"

The script groups PowerShell variants into sigma_powershell.xml and registry variants into sigma_registry.xml; other categories/services retain their own output file. Known Sysmon and Windows-channel parents are applied from the mapping table. Unmapped services use the configured fallback parent and are flagged in the console summary. You can override a specific category with --parent-sid CATEGORY=SID, or disable generic fallback with --no-fallback-parent.

The following names are representative of the category files visible in your terminal output. The exact set can change when SigmaHQ adds, removes, or reorganizes Windows logsource categories and services:

output_xml/
├── sigma_application.xml
├── sigma_appmodel.xml
├── sigma_appxdeployment_server.xml
├── sigma_appxpackaging_om.xml
├── sigma_bits_client.xml
├── sigma_capi2.xml
├── sigma_certificateservicesclient_lifecycle_system.xml
├── sigma_codeintegrity_operational.xml
├── sigma_create_remote_thread.xml
├── sigma_create_stream_hash.xml
├── sigma_diagnosis_scripted.xml
├── sigma_dns_client.xml
├── sigma_dns_query.xml
├── sigma_dns_server.xml
├── sigma_driver_framework.xml
├── sigma_driver_load.xml
├── sigma_file_access.xml
├── sigma_file_change.xml
├── sigma_file_delete.xml
├── sigma_file_executable_detected.xml
├── sigma_firewall_as.xml
├── sigma_lsa_configuration.xml
├── sigma_lsa_server.xml
├── sigma_microsoft_servicebus_client.xml
├── sigma_msexchange_management.xml
├── sigma_network_connection.xml
├── sigma_ntlm.xml
├── sigma_openssh.xml
├── sigma_pipe_created.xml
├── sigma_powershell.xml
├── sigma_process_access.xml
├── sigma_registry.xml
├── sigma_security_mitigations.xml
├── sigma_security.xml
├── sigma_shell_core.xml
├── sigma_smbclient_security.xml
├── sigma_sysmon_error.xml
├── sigma_sysmon.xml
├── sigma_system.xml
├── sigma_taskscheduler.xml
├── sigma_terminalservices_localsessionmanager.xml
├── sigma_windefend.xml
├── sigma_windows.xml
├── sigma_wmi_event.xml
└── sigma_winrm.xml

sigma_rule_ids.json

The converter also writes an XML file for a discovered bucket even if all rules in that bucket are skipped as unsupported. In that case, the file contains an empty Wazuh group and the console shows 0 rules; file presence means the category was discovered, not that detections were translated.

The console summary reports the number of Sigma rules converted, Wazuh XML rules built, excluded source files, unsupported/skipped rules, category files written, and any rules using the generic fallback parent. A skipped rule is a review item—not a conversion failure to work around by weakening the mapping.

6. Validate generated XML locally

Validate every generated file before copying anything to the Wazuh Manager:

set -e
for file in ~/sigma-wazuh-custom/output_xml/sigma_*.xml; do
  xmllint --noout "$file"
  printf '[OK] %s\n' "$(basename "$file")"
done

xmllint confirms XML well-formedness only. It does not validate Wazuh rule semantics, parent SIDs, decoded field names, PCRE2 behavior, or the detection outcome.

7. Stage and validate on Wazuh Manager

Back up the existing rules and transfer the candidate files to the manager. Use a staging directory first, then install the files under the custom rules directory:

# On the build workstation
scp ~/sigma-wazuh-custom/output_xml/sigma_*.xml \
  user@<WAZUH_MANAGER_IP>:/tmp/

# On the Wazuh Manager
sudo install -o wazuh -g wazuh -m 660 \
  /tmp/sigma_*.xml /var/ossec/etc/rules/

Run the Wazuh rule-engine configuration test before restarting the manager:

sudo /var/ossec/bin/wazuh-analysisd -t

If compilation fails, do not restart the manager. Review the reported file and line, correct the candidate XML, and repeat the test. Only restart after the analysis daemon test completes successfully:

sudo systemctl restart wazuh-manager
sudo systemctl is-active wazuh-manager

8. Validate detection logic with wazuh-logtest

Compilation does not prove a detection works. Run wazuh-logtest and feed it a representative event captured from an endpoint. The following is a Sysmon Event ID 3 field-shape example for a network connection; it is not a guaranteed match for a particular Sigma rule:

{"win":{"system":{"providerName":"Microsoft-Windows-Sysmon","eventID":"3","channel":"Microsoft-Windows-Sysmon/Operational","computer":"WKSTN-01"},"eventdata":{"image":"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe","sourceIp":"192.0.2.10","sourcePort":"51432","destinationIp":"203.0.113.10","destinationPort":"4444","protocol":"tcp","initiated":"true"}}}

For each rule you intend to enable, test both a matching event and a benign near-match. Review all three phases:

In the Wazuh Dashboard, use the rule group to review the generated detections:

rule.groups: "sigma_wazuh"

Keep the test evidence with the rule file: Sigma source UUID, source filename, generated Rule ID, test event, expected result, observed result, and reviewer decision.

9. Known limitations and safe operating practices

Do not treat a successful XML parse or a clean wazuh-analysisd -t result as approval for unattended deployment. The final acceptance criterion is correct matching against representative telemetry, with no unexpected matches against benign events.

10. Key lessons

11. Official references

DETECTION ENGINEERING · SIEM AUTOMATION

Building a practical Sigma-to-Wazuh pipeline or tuning detections at scale?

Connect to discuss Wazuh engineering, detection development, SOC automation, and SIEM/SOAR operations.