# wazuh-sigma-rules-mitre-attack

Companion files for the lesson **Sigma Rules in Wazuh and ATT&CK Coverage**.

Lesson: <https://maheshtata.com/wazuh/wazuh-sigma-rules-mitre-attack/>

Tested against: Wazuh 5.0 (beta5 documentation, September 2026). Wazuh 5.0 is still in beta.

## Files

| File | What it is |
| --- | --- |
| `export-attack-layer.py` | Builds a MITRE ATT&CK Navigator layer (format v4.5) from your Sigma rules or from your findings |

## Two modes, two different questions

```bash
# What the rule catalogue claims to cover. Needs PyYAML.
python3 export-attack-layer.py --from-rules ./rules -o claimed.json

# What the deployment has actually observed. Standard library only.
export WAZUH_INDEXER_USER=<user>
export WAZUH_INDEXER_PASSWORD=<password>
python3 export-attack-layer.py --from-findings \
  --url https://127.0.0.1:9200 --since now-30d --insecure -o observed.json
```

Open either file in the ATT&CK Navigator with **Open Existing Layer > Upload from local**. Load
both and use the Navigator's layer-comparison feature to see techniques you have rules for but
have never seen fire, which is usually the more interesting list.

## Options

| Flag | Default | Notes |
| --- | --- | --- |
| `--from-rules DIR` | | Walks `*.yml` and `*.yaml`, reads `mitre.technique` and `mitre.subtechnique` |
| `--from-findings` | | Terms aggregation over the findings data streams |
| `--url` | `https://127.0.0.1:9200` | Wazuh indexer |
| `--index` | `wazuh-findings-v5-*` | Narrow to one category if you want a per-category layer |
| `--since` | `now-30d` | Date math on `@timestamp` |
| `--field-suffix` | empty | Set to `.keyword` if the mitre fields are not aggregatable in your mapping |
| `--bucket-size` | `1000` | Terms aggregation size |
| `--insecure` | off | Skip TLS verification, for the installer's self-signed certificates |
| `--attack-version` | unset | Pin the layer to an ATT&CK version; omitted means the Navigator's current one |
| `-o` | `wazuh-attack-layer.json` | Output path |

Credentials come from `--user` and `--password` or from `WAZUH_INDEXER_USER` and
`WAZUH_INDEXER_PASSWORD`. Use a read-only indexer account, not `admin`.

## Why there is no "read the rules from the API" mode

The Wazuh 5.0-beta Content Manager API specification defines `POST`, `PUT` and `DELETE` on
`/_plugins/_content_manager/rules`, and `PUT`/`DELETE` on `/_plugins/_content_manager/rules/{id}`,
but no `GET` or list operation. There is no documented endpoint that returns the rule catalogue, so
the script reads rules from source control instead. Keeping custom rules in git is the right answer
regardless: the Draft to Test to Custom promotion chain is a deployment mechanism, not a version
control system.

`POST /_plugins/_security_analytics/rules/_search` does exist and returns Security Analytics rule
documents, but the documented response carries the upstream Sigma body and Sigma `tags` rather than
the Wazuh `mitre:` block, so it is not a reliable source for technique identifiers. Re-check that at
general availability.

## Field mapping caveat

`--from-findings` aggregates on `wazuh.rule.mitre.technique.id` and
`wazuh.rule.mitre.subtechnique.id`.

Verified on a Wazuh 5.0.0-beta5 lab on 23 September 2026: `mitre` is an object on the
finding, not a flat list of identifiers. A real finding from the shipped file integrity
content carried `wazuh.rule.mitre.technique.id` and `.name`, plus
`wazuh.rule.mitre.tactic.id` and `.name`, and no `subtechnique` key at all. The MITRE
ATT&CK dashboard filters on `wazuh.rule.mitre.technique.id: exists`, which is the same
path. An aggregation on the bare `wazuh.rule.mitre.technique` therefore returns nothing
useful, and the subtechnique aggregation may legitimately return no buckets.

A terms aggregation needs an aggregatable mapping. If the fields are mapped as text in your index
templates, the request returns `400` and the script tells you to retry with `--field-suffix
.keyword`. Check the mapping before assuming zero coverage.

## What the score means

In `--from-rules` mode the score is the number of rules mapped to a technique. In `--from-findings`
mode it is the finding count in the window. Neither is a maturity measure. A technique with fifty
rules and no true positives is worse than a technique with one rule that works, and a technique with
a large finding count is usually a noisy rule rather than a well-covered one. Use the layer to find
the empty cells that matter for your threat model, not to produce a percentage.

## Sources

- ATT&CK Navigator Layer File Format v4.5:
  <https://github.com/mitre-attack/attack-navigator/blob/master/layers/spec/v4.5/layerformat.md>
- Wazuh Sigma rule structure and the `mitre` block:
  <https://documentation.wazuh.com/5.0-beta/user-manual/data-analysis/rules.html>
- Security Analytics detector constraints:
  <https://documentation.wazuh.com/5.0-beta/user-manual/wazuh-indexer/modules/security-analytics.html>
