Why Detection Engineering Labs?
Writing a detection rule that compiles is easy. Writing one that matches exactly the malicious events in a corpus of hundreds of event logs — or all variants of a malware family across thousands of files — is a different discipline entirely.
This write-up covers four labs from the CyberDefenders Detection Engineering track. Two labs focus on Sigma rules against Windows event-log telemetry; two focus on YARA rules against malware samples and their variants.
Turn adversary tradecraft into validated detection logic
Research the technique, find the forensic residue it leaves, express that residue as a rule, and prove the rule works against real historical data — including the false positives that naive rules would generate.
The Four Labs
SigmaPredator
Deconstruct Windows Event Log Clearing (T1070.001) across CLI, WMI and PowerShell, then validate Sigma rules with Chainsaw against 333 historical EVTX files.
Sigma 101
Analyze three suspicious log sets — a masqueraded VBScript, a process-hollowing chain and a PowerShell CLM bypass — and write Sigma rules that match them exactly.
Yara Wizards & Yara101
Statically analyze an SFX/CAB loader, LockBit ransomware and a family of .NET stealers, then author YARA rules that detect every variant with zero false positives.
Honest Write-Up Policy
This article documents the real workflow — including the rules that failed, the silent parser rejections, and the debugging that led to working detections. Detection engineering is an iterative craft, and the dead ends are where the lessons live.
Environments & Toolset
The four labs run on three different environments: two Windows Server machines for the Sigma and Yara Wizards labs, and an Ubuntu analysis box for Yara101. All analysis was performed inside the isolated lab VMs.
Core Tools
Chainsaw
WithSecure's fast EVTX hunting engine. Loads Sigma rules through a field-mapping file and runs them against entire event-log corpora in seconds.
Sigma
The vendor-agnostic detection rule format for log telemetry: logsource + detection + condition, convertible to any SIEM backend.
YARA + Validators
Pattern matching over files and memory. Lab-provided
validators (YaraValidator,
verify) score rules against variant
corpora and release the flag only on exact coverage.
DIE / strings / FLOSS
Static triage: packer and entropy analysis with Detect It Easy, ASCII/UTF-16 string extraction, and ML-ranked string relevance with Stringsifter.
A detection rule is a hypothesis about forensic residue. Validation against a real corpus is the experiment that proves or falsifies it.
Deconstructing Windows Event Log Clearing
SigmaPredator places you inside the CyberPredator Enterprise as a Detection & Hunting Engineer. The target technique is Indicator Removal: Clear Windows Event Logs (T1070.001) — observed in campaigns attributed to APT28, APT41 and Aquatic Panda, and used to degrade investigative visibility after compromise.
The first phase maps the full attacker toolset for clearing logs, together with the telemetry each method leaves behind.
Figure 1 — The lab workstation: a 333-file Historical Logs corpus and the Chainsaw toolset.
Clearing Methods & Their Telemetry
Research across Microsoft Learn and the MITRE ATT&CK technique page produced a complete map of the tradecraft:
| Vector | Attacker Tooling | Telemetry Source |
|---|---|---|
| Native CLI | wevtutil cl / clear-log |
Process creation (Sysmon 1 / 4688) |
| WMI | wmic nteventlog ... call ClearEventLog |
Process creation — the nteventlog alias of Win32_NTEventlogFile appears in the command line |
| PowerShell cmdlets | Clear-EventLog, Remove-EventLog |
PowerShell Script Block Logging — Event ID 4104 |
| .NET APIs | EventLogSession.ClearLog, EventLog.Clear |
Script Block Logging (unless Constrained Language Mode blocks them) |
| Residual audit events | — (generated by Windows itself) | 1102 (Security log cleared) · 104 (System/other log cleared) |
Log clearing is self-incriminating.
Whatever tool the attacker uses, Windows writes a "log was cleared" record — Event ID 1102 into Security, Event ID 104 into System — the moment the wipe completes. These residual events became the backbone of the highest-fidelity rule in this lab.
Answer-Mask Driven Research
Several research questions enforce strict answer formats
(e.g. a 10-character mask rejecting
Win32_NTEventlogFile but accepting its WMIC
alias nteventlog). Reading the mask before
answering saves a surprising amount of time — the format
often tells you which representation of the fact
the lab wants.
Debugging "No Valid Detection Rules"
The Rule Development phase validates every Sigma rule with Chainsaw against the 333-file EVTX corpus. This is where the lab stopped being easy: for a long stretch, Chainsaw v2.9.2 rejected every single rule with the same two messages.
[x] No valid detection rules were found in the provided paths
[!] : failed to parse sigma rule
[+] Validated 0 detection rules out of 1
Figure 2 — Chainsaw silently rejecting a syntactically valid Sigma rule.
Systematic Elimination
Instead of guessing, every variable was isolated and tested independently:
Format-Hex confirmed clean ASCII/UTF-8, no BOM, LF and CRLF both tested.
sigma-event-logs-all.yml dissected with Select-String: it maps rules by logsource.category / logsource.service preconditions, so an unmapped logsource is silently dropped.
chainsaw hunt [OPTIONS] [RULES] [PATH]...; -s/--sigma + --mapping, full absolute paths for logs, rules and mapping.
chainsaw lint --kind sigma <rule> reproduces the parser verdict without touching the corpus.
Select-String -Path "mappings\sigma-event-logs-all.yml" -Pattern "service:|category:"
Get-Content "mappings\sigma-event-logs-all.yml" -TotalCount 30
Figure 3 — The mapping file's supported logsource categories and services, extracted line by line.
Root Cause
The breakthrough came from rebuilding the rule with the
complete Sigma metadata skeleton — including the
fields that feel optional. The moment description,
author, date, modified,
tags and falsepositives were present,
Chainsaw loaded the rule and the corpus lit up.
Chainsaw v2.9.2 drops Sigma rules with incomplete metadata — without telling you which field is missing.
A minimal title / logsource / detection / condition
rule parses fine in other tooling but is reported here only as
"failed to parse sigma rule". Always author rules with the
full skeleton, and keep lint --kind sigma in the
loop as a fast pre-flight check.
chainsaw.exe hunt "C:\Users\Administrator\Desktop\Start Here\Historical Logs" `
--sigma "C:\Users\Administrator\Desktop\rules\rule.yaml" `
--mapping "C:\...\chainsaw\mappings\sigma-event-logs-all.yml"
Three Detection Layers, Three Rules
The lab asks for three Sigma rules, one per telemetry layer, each validated for a specific timestamp in the corpus.
Rule 1 — PowerShell Script Block Logging (4104)
The scenario notes that PowerShell runs in Constrained Language Mode, so the rule deliberately targets only the native cmdlet family and ignores .NET API strings that CLM would block anyway.
title: Suspicious PowerShell Event Log Tampering via Script Block Logging
id: cc36992a-4671-4f21-a91d-6c2b72a2edf5
status: stable
description: >
Detects PowerShell Script Block Logging events (Event ID 4104) containing
cmdlets commonly used to clear Windows Event Logs.
author: Ahmet Can Aytekin
date: 2026-10-05
modified: 2026-10-05
tags:
- attack.defense-evasion
- attack.t1070.001
logsource:
product: windows
service: microsoft-windows-powershell/operational
detection:
selection:
EventID: 4104
ScriptBlockText|contains:
- 'Clear-EventLog'
- 'Remove-EventLog'
- 'Limit-EventLog'
- 'Clear-WinEvent'
condition: selection
falsepositives:
- Legitimate administrative maintenance scripts
level: high
Figure 4 — One detection across 333 artifacts: the earliest ScriptBlock match.
Rule 2 — Native CLI Clearing via Process Creation
The second rule is built around two anchors that make it
resilient against evasion: binary identity
(OriginalFileName, so a renamed copy of wevtutil
is still caught) and command-line intent
(the destructive sub-commands: cl,
clear-log, plus the config-tampering
sl/set-log and lfn:).
Two OR'd selections cover both telemetry sources — Sysmon
EID 1 and native Security EID 4688:
title: Suspicious EventLog Clearing or Configuration Change Activity
id: cc36992a-4671-4f21-a91d-6c2b72a2edf6
status: stable
description: >
Detects wevtutil usage to clear or reconfigure event logs via Sysmon (EID 1)
or Windows Security process creation events (EID 4688).
author: Ahmet Can Aytekin
date: 2026-10-05
modified: 2026-10-05
tags:
- attack.defense-evasion
- attack.t1070.001
logsource:
product: windows
detection:
selection_sysmon:
EventID: 1
Image|endswith: '\wevtutil.exe'
OriginalFileName: 'wevtutil.exe'
CommandLine|contains:
- 'clear-log '
- 'cl '
- 'set-log '
- 'sl '
- 'lfn:'
selection_security:
EventID: 4688
NewProcessName|endswith: '\wevtutil.exe'
CommandLine|contains:
- 'clear-log '
- 'cl '
- 'set-log '
- 'sl '
- 'lfn:'
condition: selection_sysmon or selection_security
falsepositives:
- Legitimate administrative log maintenance
- Automated log-rotation or archival processes
level: high
The rule returns 15 detections across both telemetry sources, with zero false positives from routine maintenance. The question asks for the latest match this time — sorting the Chainsaw table by timestamp descending gives the answer.
Figure 5 — 15 wevtutil detections across Sysmon and Security telemetry; the most recent row answers the question.
Rule 3 — Residual Audit Events (1102 / 104)
The first attempt — service: security with
Event ID 1102 only — returned zero hits, and a
104-only variant surfaced 115 events polluted by
RemoteDesktopServices-RdpCoreTS noise (RDP's own,
unrelated Event ID 104). The final rule pins the provider and
covers both IDs:
title: Windows Event Log Cleared
id: cc36992a-4671-4f21-a91d-6c2b72a2edf7
status: stable
description: >
Detects native audit events generated when a Windows Event Log is cleared:
1102 (Security) and 104 (System), scoped to the genuine Eventlog provider.
author: Ahmet Can Aytekin
date: 2026-10-05
modified: 2026-10-05
tags:
- attack.defense-evasion
- attack.t1070.001
logsource:
product: windows
detection:
selection_cleared:
Provider_Name:
- 'Microsoft-Windows-Eventlog'
- 'Microsoft-Windows-Security-Auditing'
EventID:
- 104
- 1102
condition: selection_cleared
falsepositives:
- Legitimate administrative log clearing
level: high
| Rule Iteration | Detections | Verdict |
|---|---|---|
| 1102 only (service: security) | 0 | Logsource too narrow |
| 104 only | 115 | RdpCoreTS false positives |
| 1102 + 104, provider-scoped | 132 | Clean coverage |
Event ID alone is not an identity.
Event ID 104 exists in multiple providers with completely
different meanings. Scoping on
Provider_Name: Microsoft-Windows-Eventlog
eliminated an entire class of RDP false positives — and
turned a wrong answer into the correct earliest timestamp,
which came from a 1102 record the
104-only rule could never see.
Masqueraded VBScript Spawning PowerShell
Sigma 101 changes the format: three small EVTX files, one per
level, and a local web-based Sigma compiler
(http://127.0.0.1:5500/). You pick a level, paste
a rule, press Run — and the backend tells you
whether the rule matches exactly the right events.
Level 1's log holds three Sysmon Event ID 1 records. Dumping their EventData with PowerShell exposes the malicious one immediately:
Get-WinEvent -Path ".\Levels\1\1.evtx" | ForEach-Object {
$x = [xml]$_.ToXml()
$d = @{}; $x.Event.EventData.Data | ForEach-Object { $d[$_.Name] = $_.'#text' }
[pscustomobject]@{
Time = $_.TimeCreated
Image = $d['Image']
CommandLine = $d['CommandLine']
Parent = $d['ParentImage']
}
} | Format-List
A .ico file forced to execute as VBScript
via /E:vbs, which in turn launches a
Base64-obfuscated PowerShell stage that decodes a second
payload from c:\windows\temp\picture.jpg.
Classic extension masquerading.
Figure 6 — The local Sigma compiler: one rule per level, instant match feedback.
title: Level 1
id: bbce11dd-64ac-4656-96b8-753097833492
status: experimental
description: Detect Malicious PowerShell Script Execution via wscript.exe
logsource:
category: process_creation
product: windows
detection:
selection:
Image|endswith: '\wscript.exe'
ParentImage|endswith: '\explorer.exe'
CommandLine|contains:
- 'powershell -exec bypass'
- 'IEX'
condition: selection
Compiler Pedantry
The web compiler is strict about formatting: 4-space indentation and straight quotes. A semantically identical rule with 2-space indentation returned "No hits found" — the parser failed silently rather than reporting a syntax error.
Process Hollowing — Matching a Three-Event Chain
Level 2's EVTX tells a complete process-hollowing story across three Sysmon event types:
GrantedAccess 0x1fffff is PROCESS_ALL_ACCESS —
exactly what a hollowing loader requests before rewriting the
target's memory. The interesting part of this level is the
compiler's feedback loop:
| Rule Attempt | Compiler Response |
|---|---|
| Single-event rule (EID 1 parent/child only) | "Not enough matches — 1 event out of 3" |
Broad keyword on wermgr.exe |
"Your rule is very generic and matches 5 events" |
| Three event-specific selections, OR'd | Exact 3/3 — Level 2 flag |
title: Process Hollowing Via Rundll32
id: 7c0e2a84-5bd3-4e26-9f41-8b3f2a6d4c11
status: experimental
description: Detects the full process hollowing chain where rundll32 hollows wermgr.exe
logsource:
product: windows
service: sysmon
detection:
selection1:
Image|endswith: '\wermgr.exe'
ParentImage|endswith: '\rundll32.exe'
selection2:
SourceImage|endswith: '\rundll32.exe'
TargetImage|endswith: '\wermgr.exe'
selection3:
Image|endswith: '\wermgr.exe'
TargetObject|contains: 'Internet Settings'
condition: selection1 or selection2 or selection3
1 / 3
Misses the ProcessAccess and Registry stages
Incomplete behavioral coverage5 events
Sweeps in benign wermgr activity
False-positive generatorEach selection must carry an event-unique discriminator.
Pairing every image match with a second field that only the intended event possesses — the rundll32 parent, the Source/Target pair, the registry path — is what collapsed 5 matches down to exactly 3.
Defense Evasion — PowerShell CLM Bypass
Level 3 contains a single Sysmon Event ID 12 record, and its
own RuleName field spells out the technique:
"Defense Evasion — PowerShell CLM Setting Changed".
powershell.exe deletes the
__PSLockdownPolicy value — the environment
flag that forces Constrained Language Mode. Removing it
returns PowerShell to Full Language Mode, re-enabling
.NET invocation for follow-on tooling.
title: PowerShell Lockdown Policy Removal
id: 7c0e2a84-5bd3-4e26-9f41-8b3f2a6d4c13
status: experimental
description: Detects deletion of the PSLockdownPolicy value to bypass Constrained Language Mode
logsource:
product: windows
service: sysmon
detection:
selection:
Image|endswith: '\powershell.exe'
TargetObject|contains: '__PSLockdownPolicy'
condition: selection
One event, two discriminating fields, an exact 1/1 match — and the Level 3 flag closes the lab.
An SFX Cabinet With 99% Packed Entropy
Yara Wizards hands you a single MalwareSample.exe
and a validator that only releases the flag when your YARA rule
matches multiple variants of the same family.
First step: understand what the sample actually is.
Detect It Easy
DIE identifies the sample in one scan: a 2.54 MiB PE32 built with Visual Studio 2022, flagged as a Sfx: Microsoft Cabinet — a self-extracting archive that unpacks and installs itself with no external extraction program, carrying an embedded CAB (LZX, 38.7%, 7 files).
Figure 7 — DIE: SFX Microsoft Cabinet with an embedded 7-file LZX archive.
Entropy Analysis
The entropy diagram makes the structure obvious: a tiny
low-entropy PE stub (.text at 6.27,
.data at 4.97) followed by a near-8.0 plateau —
the compressed cabinet that makes up almost the entire file.
Figure 8 — Entropy 7.98612: a thin extractor stub fronting a wall of compressed data.
Why Entropy Matters for YARA
At 7.99 entropy, almost nothing inside the payload is string-matchable in its packed form. Any useful signature must target the stub, the CAB structure, or the extracted contents — which is exactly where the analysis went next.
Carving the Cabinet, Reading the Batch
expand refuses to read the CAB through the SFX stub,
so the cabinet was carved manually: locate the
MSCF magic, write everything from that offset into
a standalone .cab, then expand it.
$b = [System.IO.File]::ReadAllBytes("...\MalwareSample.exe")
for ($i=0; $i -lt $b.Length-4; $i++) {
if ($b[$i] -eq 0x4D -and $b[$i+1] -eq 0x53 -and
$b[$i+2] -eq 0x43 -and $b[$i+3] -eq 0x46) {
"MSCF offset: $i"; $off = $i; break
}
}
# → MSCF offset: 141424
[System.IO.File]::WriteAllBytes("C:\extracted\embedded.cab", $b[$off..($b.Length-1)])
expand -F:* C:\extracted\embedded.cab C:\extracted
Figure 9 — The seven extracted cabinet files: one batch script, four split payload parts and two .dat companions.
The Dropper Logic — Three Lines of Batch
rem 11747112
copy /b eqnyiodbs.dat + eqnyiodbs.dat.1 + eqnyiodbs.dat.2 + eqnyiodbs.dat.3 fjlpexyjauf.exe
fjlpexyjauf.exe lknidtnqmg.dat %1%
The chain is now fully reconstructed — statically, without running a single malicious byte:
| Artifact | Value |
|---|---|
| Dropper batch file | bqwybceocy.bat |
| RUNPROGRAM command line (from stub strings) | cmd.exe /d /c bqwybceocy.bat 3991425476 |
| Reassembled final executable | fjlpexyjauf.exe |
| Encrypted payload argument | lknidtnqmg.dat + numeric key via %1% |
| Drop path (session-aware Temp) | C:\Users\Administrator\AppData\Local\Temp\2\IXP000.TMP |
IXP000.TMP — and the Terminal Server twist.
The wextract-style stub always unpacks into an
IXP000.TMP directory under the user's Temp —
but on this Server 2016 host, the RDP session inserts a
numeric session folder first. Listing
%LOCALAPPDATA%\Temp revealed a single
directory named 2, completing the full path
the lab's answer mask demanded.
From Entropy Heuristics to the Lu0Bot Flag
The validated rule combines the two strongest observations
from the analysis: the near-8.0 entropy of the packed body,
and the execution-chain strings — the batch launcher command
plus the split .dat fragment extensions that
every variant of this loader reassembles with
copy /b.
import "math"
rule yara_rule
{
meta:
author = "CyberDefenders"
date = "2025-01-01"
strings:
$str = "cmd.exe /d /c bqwybceocy.bat"
$dat_1 = ".dat"
$dat_2 = ".dat.1"
$dat_3 = ".dat.2"
$dat_4 = ".dat.3"
$bat = ".bat" ascii
condition:
math.entropy(0, filesize) > 7 and
any of ( $str, $bat ) and
all of ( $dat_1, $dat_2, $dat_3, $dat_4 )
}
Gates on the packed/encrypted body
The split-payload reassembly fingerprint
The batch-launcher execution chain
Figure 10 — YaraValidator (-e): 28 variants detected, and the flag finally names the family.
Flag{Lu0Bot_Detected}
The flag reveals what static analysis alone could not name: this CAB-SFX loader chain is Lu0Bot, a Node.js-based bot known for exactly this delivery pattern — split .dat fragments reassembled by a randomly-named batch script.
28 hits came with 10 false positives — and that is the lesson.
Generic anchors like .bat and bare
.dat buy variant coverage but leak precision.
A hardened iteration would swap them for structural
anchors that benign software rarely combines:
uint16(0) == 0x5A4D, the embedded
MSCF cabinet magic, the
cmd.exe /d /c launcher prefix and the
IXP000.TMP staging directory. Structure
generalizes across variants; random filenames and
generic extensions do not discriminate.
LockBit With a Broken MZ Header
Yara101 moves to Ubuntu and raises the stakes: the
verify command scans your rule against a
4,178-file corpus and releases the flag only
on exact coverage. Level 1 provides a single sample that
file stubbornly calls "data".
00000000: 2d2d 2d2d 2d2d 2d2d 2d2d 2d0a 4d5a 9000 -----------.MZ..
^^^^
MZ header pushed to offset 0x0C
Eleven dashes and a newline prepend the PE — enough to break
file, most carvers, and any YARA rule that starts
with uint16(0) == 0x5A4D. The strings underneath
leave no doubt about the family:
All your files are encrypted by LockBit
LockBit Ransom · Restore-My-Files.txt
http://lockbitks2tvnmwk.onion
fsutil file setZeroData ... & Del /f /q
rule LockBit_Ransomware
{
meta:
description = "Detects LockBit ransomware variants"
author = "Ahmet Can Aytekin"
strings:
$s1 = "All your files are encrypted by LockBit" ascii wide
$s2 = "Restore-My-Files.txt" ascii wide
$s3 = "lockbitks2tvnmwk.onion" ascii wide
$s4 = "LockBit Ransom" ascii wide
condition:
2 of them
}
Why There Is No MZ Check Here
The corrupted header is the trap. Adding
uint16(0) == 0x5A4D — normally best practice —
would make the rule miss its own target sample. Level 1
rewards reading the hex dump before reaching for the
standard rule template.
Figure 11 — verify: 4,178 files scanned, exactly one match, zero false positives.
Level 1 flag: yara_level_1_completed_4de1e9160
The 2 of them condition hit the target and
nothing else across the entire corpus — the precision/recall
balance a production rule needs.
Four .NET Stealers, One Rule
Level 2 supplies four PE32 Mono/.NET assemblies. FLOSS + Stringsifter triage paints an unmistakable infostealer profile:
| Capability | Evidence | Present In |
|---|---|---|
| Victim fingerprinting | api.ipify.org · api.ip.sb · ipinfo.io · "Total of RAM" |
All 4 |
| Credential decryption | bEncryptedData · <encrypted_key>k__BackingField · bcrypt.dll |
All 4 |
| C2 channel | net.tcp:// (RedLine-style WCF transport) |
All 4 |
| FTP credential theft | {0}\FileZilla\recentservers.xml · sitemanager.xml (UTF-16) |
sample_1/3/4 ASCII; sample_2 Unicode-only |
| Crypto-wallet targeting | Base64 blob of 30+ extensionID|WalletName pairs (Metamask, TronLink, Phantom, Coinbase…) |
sample_2 |
strings -el sample_2 | grep '^ZmZuYmVs' | base64 -d | head
# ffnbelfdoeiohenkjibnmadjiehjhajb|YoroiWallet
# ibnejdfjmmkpcnlpebklmnkoeoihofec|Tronlink
# nkbihfbeogaeaoehlefnkodbefgpgknn|Metamask
# ...
Figure 12 — Ranked strings across the four samples: IP-check services, encrypted-key fields and wallet targets.
The Final Rule — Strings + Offset Condition
The level explicitly requires a string-offset condition.
Unlike Level 1, all four samples here carry intact MZ headers —
so $mz at 0 both satisfies the requirement and,
elegantly, excludes the header-corrupted LockBit sample from
this family's rule.
rule DotNet_Stealer_Variants
{
meta:
description = "Detects .NET stealer variants targeting browsers, FTP clients and crypto-wallet extensions"
author = "Ahmet Can Aytekin"
strings:
$mz = { 4D 5A }
$s1 = "bEncryptedData" ascii
$s2 = "k__BackingField" ascii
$s3 = "api.ip.sb" ascii wide
$s4 = "net.tcp://" ascii wide
condition:
$mz at 0 and 2 of ($s*)
}
The required offset condition — PE anchored at byte zero
Compiler-generated property names shared by every variant
Catches all four, tolerates per-variant string stripping
The common denominator is rarely the obvious string.
"FileZilla" looked like the perfect anchor — until
sample_2 turned out to carry it only as a
UTF-16 path template, invisible to a default ASCII scan.
The strings that actually spanned all four variants were
the unglamorous .NET internals:
bEncryptedData and the backing-field name the
compiler generates for the stealer's key property.
What Four Labs Teach About Detection Engineering
Validators are silent until they aren't
Chainsaw dropped metadata-incomplete rules without naming the missing field; the Sigma compiler swallowed indentation errors as "no hits". Build a lint step into the loop before trusting any zero-match result.
Author rules with the full skeleton
description, author, date, tags, falsepositives — treat the "optional" Sigma fields as mandatory. Tooling in the wild (and in these labs) assumes they exist.
Field + discriminator, never field alone
Event ID 104 belongs to multiple providers; wermgr.exe appears in benign telemetry. Pairing every match with a second event-unique field is what separates exact detection from noise.
Sign structure, not randomness
Per-build random filenames die with the build. Format magics, launcher syntax, staging paths and compiler-generated .NET internals are what survive across variants.
Scan ASCII and UTF-16 — always
The FileZilla paths and the wallet blob lived only in
wide strings. A default strings run would
have missed both the IOCs and a wrong-answer trap.
Read the bytes before the template
One hex dump revealed the shifted MZ header that would
have broken every textbook YARA rule. Ten seconds of
xxd beat an hour of condition debugging.
Techniques Covered Across the Series
Clear Windows Event Logs
SigmaPredator's entire arc: wevtutil, WMI, PowerShell cmdlets and the residual 1102/104 audit events.
DETECTED — 3 SIGMA RULESProcess Hollowing
rundll32 → wermgr.exe with PROCESS_ALL_ACCESS, reconstructed across Sysmon 1/10/12 in Sigma 101.
DETECTED — 3-SELECTION RULEMasquerading & Impairing Defenses
A .ico executed as VBScript, and deletion of __PSLockdownPolicy to escape Constrained Language Mode.
DETECTED — SIGMA 101 L1/L3Credential Theft & Data Encryption
.NET stealers harvesting browser, FTP and wallet credentials; LockBit encryption with Tor-based ransom workflow — both signed with multi-variant YARA rules.
DETECTED — YARA101 L1/L2From Tradecraft to Signatures That Hold
Across four labs, the same loop repeated itself: research the technique, find its residue, express it as a rule — and then let a validator break that rule until it matched reality instead of assumptions.
The finished detections — three Sigma rules for event-log clearing, three for a lateral-movement chain, and multi-variant YARA signatures for an SFX loader, LockBit and a .NET stealer family — are less valuable than the debugging scars behind them. Silent parser rejections, provider-ambiguous Event IDs, Unicode-only strings and a sabotaged MZ header are precisely the obstacles production detection engineering is made of.
A rule that merely compiles describes your intent. A rule that survives validation describes the adversary.