Emulate adversary techniques, then prove the detections fire. This is the validation half of detection-as-code: I run ATT&CK techniques against an instrumented Ubuntu endpoint enrolled in my SOC automation lab, and confirm each one raises the alert it's supposed to - mapped to...
Emulate adversary techniques, then prove the detections fire. This is the validation half of detection-as-code: I run ATT&CK techniques against an instrumented Ubuntu endpoint enrolled in my SOC automation lab, and confirm each one raises the alert it's supposed to - mapped to the right technique, at a severity that would actually page someone.
Writing a detection is a hypothesis. This is the test.

Every row is an atomic I executed on the endpoint and the rule that caught it - five of
the six are detections I wrote (100410-100413) plus the brute-force rule, all on the
purple-target agent, all ATT&CK-tagged.
flowchart LR
A[Atomic Red Team atomics<br/>run on purple-target VM] --> B[Wazuh agent<br/>auth log + real-time FIM]
B -->|1514/tcp| C[Wazuh manager<br/>analysisd: built-in + custom rules]
C --> D[Indexer]
D --> E[Dashboard<br/>ATT&CK-mapped alerts]
F[MITRE Caldera<br/>adversary emulation] -.-> B
The target is a dedicated Ubuntu 22.04 VM (not a container - kernel-level telemetry needs a real kernel). It runs the Wazuh agent shipping auth logs and real-time file integrity monitoring on the paths attackers use for persistence. Caldera is available for graph-based adversary emulation; the day-to-day validation uses Atomic Red Team because it maps cleanly one-atomic-to-one-detection.
Each technique was executed on the endpoint and confirmed in the SIEM:
| Technique | Atomic | Telemetry | Rule | Level | Result |
|---|---|---|---|---|---|
| T1110 Brute Force | 18 invalid SSH logins | auth log | 5712 (built-in) |
10 | ✅ detected |
| T1053.003 Cron persistence | drop job in /etc/cron.d |
FIM | 100410 (custom) |
10 | ✅ detected |
| T1543.002 Systemd persistence | create .service unit |
FIM | 100411 (custom) |
10 | ✅ detected |
| T1098.004 SSH authorized_keys | append attacker key | FIM | 100412 (custom) |
12 | ✅ detected |
| T1543 Tooling drop | binary into /usr/local/bin |
FIM | 100413 (custom) |
10 | ✅ detected |
| T1078 / T1548.003 | login + sudo to root | auth log | 5501 / 5402 |
3 | ✅ (informational baseline) |
The custom rules are in rules/local_purple_rules.xml;
the atomics that exercise them are in atomics/run_atomics.sh.
The persistence detections key off file integrity monitoring, not syscall auditing.
That's deliberate: FIM is Wazuh-native and works everywhere - including containers and
hardened hosts where the kernel audit framework isn't available to you - whereas auditd
execve rules silently do nothing on a host that boots with audit disabled. The auditd
ruleset I'd layer on a host that does have kernel auditing is included in
agent/auditd-purple.rules, but the validated detections
above don't depend on it.
On the endpoint (enrolled Wazuh agent + the FIM config from agent/):
sudo bash atomics/run_atomics.sh # execute the technique set
Then in the Wazuh dashboard, filter Threat Hunting → Events to
rule.id:(100410 or 100411 or 100412 or 100413 or 5712) and confirm the hits.
Caldera (adversary-emulation UI, optional):
cd caldera && docker compose up -d # http://localhost:8888
rules/local_purple_rules.xml custom FIM detections (deploy to the manager)
agent/fim-directories.xml real-time FIM config for the agent's syscheck block
agent/auditd-purple.rules auditd rules for hosts with kernel auditing
atomics/run_atomics.sh the ATT&CK technique set
caldera/docker-compose.yml MITRE Caldera server
docs/validation-matrix.md the matrix above, with notes per technique