Environment

Two VirtualBox hosts on a host-only network (192.168.56.0/24): a Windows 10 Pro 22H2 endpoint (DESKTOP-LIJD7CI, 192.168.56.20) running Sysmon v15.21 with the SwiftOnSecurity config and the Wazuh Agent, and an Ubuntu 24.10 host (192.168.56.103) running the full Wazuh 4.12 all-in-one stack — Manager, Indexer, and Dashboard. Windows Event Log and Sysmon telemetry flow over authenticated TCP 1514 into the manager, get matched against stock and custom rules, and land in the indexer for Discover/dashboard queries. This is deliberately a one-host, one-manager lab — it validates detection logic end to end, not multi-agent management at scale.

Getting there required real engineering, not just following an install guide: the reference ossec.conf documented the Sysmon forwarding block, but the actual deployed config on the endpoint didn't have it — a config-as-documentation vs. config-as-deployed drift that had to be caught with Select-String, not assumed. Filebeat's archives fileset is off by default, which silently blocks raw Discover queries even when alerts are indexing fine. A 30-day index retention policy had to be applied manually via an OpenSearch ISM policy — the default install has none, and unmanaged indices caused a real disk-pressure outage during this build (see Incident Notes below).

Verify, don't assume — scripts/healthcheck.sh

Screenshots prove a state existed once; they don't prove the pipeline still works. healthcheck.sh checks all 8 stages programmatically on demand — services, disk, rule syntax, Filebeat archives + auth, agent connectivity, Sysmon events actually present in the index, every one of the 6 custom rules firing under its own ID, and stock-rule coverage for the rest. A fresh deployment correctly shows stages 7-8 as FAIL until the attack simulation has run at least once — that's expected, not a bug.

The kill chain: 6 techniques, one causal chain

scripts/attacks.bat is not six independent technique demos run back to back — it's a single kill chain where each phase is a genuine, verified consequence of the phase before it. The brute force in Phase 2 actually succeeds against a real local admin-group test account (svcbackup), and the SAM dump in Phase 3 runs as that same compromised account, via a scheduled task, not from a pre-existing elevated shell. Every technique below is backed by a real Sysmon Event ID 1 (Process Create) record, forwarded through the agent and queried from the manager's own index — not a simulated example.

Sysmon Event ID 1 · Wazuh stock rules 92031 / 92039

Phase 1 & 6 — Discovery T1082, T1057

The chain opens and closes with reconnaissance: systeminfo, whoami /all, net user, net localgroup administrators, and tasklist /v. Discovery isn't glamorous, but it's the step every real intrusion takes before committing to anything else — and it's exactly the kind of low-and-slow activity Sysmon's full command-line capture catches that native Windows logging alone would miss entirely.

Both discovery passes are caught by Wazuh's stock ruleset without any custom work: rule 92031 (discovery activity, T1087) and the 92036/92039 family for net.exe account-enumeration commands. A custom rule (100014) was also written specifically for the generic discovery command pattern and is independently verified firing 24 times against the live indexer.

Rule IDs: stock 92031, 92036, 92039 · custom 100014 (24 verified hits)
Sysmon Event ID 1 + Security 4624/4625 · Wazuh custom rules 100016 / 100017

Phase 2 — Brute Force T1110.001

Five real failed IPC$ authentication attempts against the local admin-group account svcbackup, followed by a sixth attempt using its actual password — this brute force genuinely succeeds: a real Windows Security 4624 (logon success) follows five real 4625 (logon failure) events, not an assumption dressed up as one. The critical detection insight here is that Sysmon Event ID 1 alone only proves net.exe ran — it says nothing about whether the authentication attempt it made actually succeeded or failed. Real T1110 detection had to correlate against Windows Security Event ID 4625, the actual auth-failure event, not pattern-match a command line.

Custom rule 100016 answers "did a brute-force pattern occur" by correlating 5+ stock-rule-60122 (4625) hits within a 120-second window on the same agent. It says nothing about whether any guess worked — that distinction is exactly what a real analyst has to make. Rule 100017 automates that check: it fires only when a real 4624 logon success follows a 100016 alert on the same endpoint, confirming the brute force didn't just get attempted, it succeeded. Getting this right surfaced a real bug in the simulation itself: an earlier version replaced the 5th failed guess with the successful one, leaving only 4 real failures — one short of the rule's own threshold — so neither rule fired until the script was fixed to send all 5 genuine failures first.

Rule IDs: custom 100016 (5 hits, level 10), 100017 (11 hits, level 12)
Sysmon Event ID 1 · Wazuh stock rule 92026 (level 14) · custom rule 100018

Phase 3 — Credential Dumping (SAM) + Scheduled Task Abuse T1003.002, T1053.005

The SAM/SYSTEM hives are exported with reg.exe save HKLM\SAM ... — but the part worth documenting is how it's run: as svcbackup, the account just compromised in Phase 2, via a Task Scheduler job (schtasks /ru svcbackup /rp ... /rl highest), not from the script's own pre-existing elevated context. The first version of this phase tried runas /savecred instead, and it genuinely failed: a local-admin account accessed through a secondary runas logon gets Windows' UAC-filtered standard token, not a full admin token, so reg save HKLM\SAM failed outright with a missing-privilege error — even though the account is a real administrator. A scheduled task created with /rl highest gets a genuine, non-filtered token with no interactive UAC prompt required, which is exactly why real intrusions abuse Task Scheduler for privilege operations (T1053.005 in its own right).

The side effect of that workaround is itself a detection opportunity: creating the scheduled task puts svcbackup's actual password in cleartext on the command line (/rp Summer2026!), fully visible to Sysmon Event ID 1. Custom rule 100018 turns that side effect into an alert — critical severity, verified firing exactly once, one second after the brute-force-success confirmation. The SAM dump itself is caught by Wazuh's stock ruleset with zero custom work: rule 92026, level 14 — one step below Wazuh's maximum severity, and the loudest single alert in the whole lab.

Rule IDs: stock 92026 (13 hits, level 14) · custom 100018 (1 hit, level 12)
Sysmon Event ID 13 (Registry Set) · captured via Sysmon Event ID 1 command line

Phase 4 — Persistence via Registry Run Key T1547.001

reg add HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run /v WindowsUpdateHelper /t REG_SZ /d "C:\temp\evil.exe" /f — a Run-key entry disguised under a name ("WindowsUpdateHelper") deliberately chosen to blend into normal Windows Update noise on a casual review of the Run key list. This is one of the most common real-world persistence mechanisms precisely because it requires no special privileges and survives a simple reboot.

Sysmon's registry telemetry (Event IDs 12/13) is exactly the capability native Windows logging lacks without heavy audit-policy tuning — the SwiftOnSecurity config keeps this event type at full fidelity while filtering out the high-volume, low-signal registry noise that would otherwise drown it.

Detection: Sysmon Event ID 1 (command line) + Event ID 13 (registry value set)
Sysmon Event ID 1 · Wazuh custom rule 100012 (level 13)

Phase 5 — Defense Evasion via Encoded PowerShell T1059.001

powershell -ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden -EncodedCommand aQBwAGMAbwBuAGYAaQBnAA== — hidden window, bypassed execution policy, Base64-encoded payload. This is the technique that most directly justifies Sysmon's existence over native logging: without full command-line capture there is no way to tell a legitimate PowerShell automation script from an attacker hiding a payload behind Base64 encoding and a suppressed window.

The stock Wazuh ruleset has no rule for this exact pattern, so a custom detection was written: rule 100012 matches -EncodedCommand (case-insensitive, tolerant of .exe) directly against the Sysmon commandLine field, at level 13 — independently verified firing 8 times against the live indexer.

Rule ID: custom 100012 (8 hits, level 13)

Technique-to-rule reference

TechniqueATT&CK IDSysmon EventWazuh Rule(s)Verified Hits
System / Account DiscoveryT1082, T1087Event ID 192031, 92036, 92039, custom 100014100014: 24
Brute Force (IPC$)T1110.001Event ID 1 + Security 4625/4624custom 100013, 100016, 100017100013: 5 · 100016: 5 · 100017: 11
Credential Dumping (SAM)T1003.002Event ID 1stock 9202613
Scheduled Task AbuseT1053.005Event ID 1custom 1000181
Registry Run-Key PersistenceT1547.001Event ID 1, 13none shipped — matched by stock precedence
Encoded PowerShellT1059.001Event ID 1custom 1000128
Process DiscoveryT1057Event ID 192031 family

Three additional command-line rules (SAM-hive dump, Run-key persistence, verbose tasklist) were written, deployed, and deliberately removed: Wazuh evaluates rules in order and stops at the first match, and the stock rules already matched those exact Sysmon events first. Shipping rules that can structurally never fire is dead code, not coverage — so stock-rule coverage for those three techniques is what's actually running, confirmed both by the screenshots above and continuously by scripts/healthcheck.sh.

Compliance: Security Configuration Assessment

The Configuration Assessment module ran the CIS Microsoft Windows 10 Enterprise Benchmark v1.12.0 against DESKTOP-LIJD7CI continuously in the background (<sca><scan_on_start>yes</scan_on_start><interval>12h</interval></sca>) — 394 checks, 127 passed / 263 failed, a 32% score. Every failed check maps to an exact remediation target: a registry key or a command to verify, not just a pass/fail flag with no next step. Notably, the CIS scan independently confirms two of the exact gaps this lab's own attack simulation exploited: "Account lockout threshold" and "Account lockout duration" both failed, meaning the endpoint has no automatic lockout protection against the brute force demonstrated in Phase 2 — a compliance finding and a detection finding pointing at the same root cause.

Notable findings worth a second look

  • A brute force that actually succeeds, feeding a SAM dump that actually runs as the compromised account — most lab kill chains narrate causality; this one verifies it end to end against the live indexer, with every step tagged to the same username.
  • A UAC boundary discovered by testing, not read from documentationrunas silently produces a filtered token even for a real admin account; the workaround (scheduled tasks) is itself a named ATT&CK technique and its own detection opportunity.
  • Process-creation telemetry alone cannot detect brute force — Sysmon Event ID 1 only proves a logon attempt happened, not whether it succeeded; real T1110 detection required correlating against the actual Windows Security auth-outcome event.
  • Dead rules were deleted, not kept as decoration — three custom rules that could structurally never fire (stock rules always won evaluation precedence first) were removed rather than left in the ruleset for appearances.

Full rule definitions, the complete attack script, the health-check tooling, and 4 additional evidence screenshots not shown above (the SOC analyst triage write-ups, process hash reference table, and incident-response troubleshooting notes) are in the repository.