Problem
AD threat hunting requires practice in an environment where you control both attacker and defender. Commercial labs are expensive and rigid; blog-post screenshots are shallow.
I wanted somewhere I could: run a real Kerberoast, watch Event 4769 arrive in Wazuh, write the Sigma rule, validate against false positives, and measure end-to-end MTTD. Fully isolated from my home network, zero recurring cost, and — critically — zero dependency on client data. Everything synthetic, everything fictional, everything RFC5737.
You don’t learn detection engineering by reading blog posts. You learn by watching the detection fail three times and figuring out why.
Architecture
A Proxmox host runs 8 VMs: a full AD domain (DC + workstations), pfSense firewall with Suricata, Wazuh manager + OpenSearch, and a Kali VM for the attacker. All traffic traverses pfSense to guarantee network visibility.
// RFC5737 IPs · fictional hostnames · zero reference to real infrastructure
Implementation
Stack chosen for zero cost and extensibility — each component can be swapped for its corporate equivalent without rewriting rules.
Sigma rules live in a public Git repo. CI converts YAML Sigma → Wazuh rules via sigma-cli. Each PR runs unit tests against synthetic logs before merging.
Detections
Five notable detections. Each one started by reproducing a real attack with public tooling, capturing the raw events, and writing the rule from scratch.
# TGS request with RC4 encryption for a service account title: Suspicious TGS Request (Kerberoasting) id: a7f4b8c1-9d2e-4c9f-8a1b-3e5d7f9a2c4d status: stable logsource: product: windows service: security detection: selection: EventID: 4769 TicketEncryption: '0x17' ServiceName|endswith: '$' condition: selection level: high tags: [attack.credential_access, attack.t1558.003]
# Directory replication rights used from non-DC host title: DCSync via Replication Rights logsource: { product: windows, service: security } detection: selection: EventID: 4662 Properties|contains: - '1131f6aa-9c07-11d1-f79f-00c04fc2dcd2' # DS-Replication-Get-Changes - '1131f6ad-9c07-11d1-f79f-00c04fc2dcd2' # DS-Replication-Get-Changes-All filter_dc: SubjectUserName|endswith: '$' condition: selection and not filter_dc level: critical tags: [attack.credential_access, attack.t1003.006]
Users with "Do not require preauth" enabled. Detection via Event 4768 without PreAuth type.
PowerShell -EncodedCommand with long base64. Sysmon Event 1, high entropy on the commandline.
pfSense logs: ≥20 SSH failures in 60s from the same IP. Automatic response: block via pfBlockerNG.
CI converts YAML Sigma into Wazuh local_rules.xml. PR → test → merge → deploy via Ansible.
Metrics
Lab metrics over the last 30 days. Research environment — not comparable to a corporate SOC (volume, endpoint variety, attacker skill are all constrained).
Sigma rules
Events/day
MTTD
TTPs covered
False positive rate
Hunts executed
Rules tested
Uptime
Lessons learned
What worked — and what didn’t. Honestly.
- Worked: versioning Sigma rules in Git with CI. A rule without tests becomes a false positive in production.
- Worked: isolating the attack zone in its own pfSense VLAN. Prevents Rubeus from leaking to the home network.
- Didn’t work (v1): trying to build endpoint telemetry without Sysmon. Native Windows event log is insufficient — Sysmon is only negotiable in environments with paid EDR.
- Didn’t work (v1): Wazuh agent in "full events" mode — OpenSearch choked at >500k events/day. Fixed by filtering at source via Wazuh ruleset.
- Lesson: detection engineering is 20% writing rules, 80% FP triage. My first Kerberoast rule broke on any AD backup — a backup-machine filter fixed it.
- Lesson: write the rule after running the attack, not before. ATT&CK docs are a starting point, not a finish line.
Links & repos
Part of the ruleset is public. Feedback and PRs welcome.