Skip to content

fix(windows): stop four Windows rules from flooding - #2784

Open
kryonsx wants to merge 4 commits into
utmstack:v11from
kryonsx:codex/windows-alert-noise-20260929
Open

kryonsx wants to merge 4 commits into
utmstack:v11from
kryonsx:codex/windows-alert-noise-20260929

Conversation

@kryonsx

@kryonsx kryonsx commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Why

Four Windows rules break the limit of 10 alerts per hour on at least one v11 deployment, and the rule flood guard switches them off. Between 2026-09-28 00:00 and 2026-09-29 13:00 UTC, on v11.2.14:

Rule Alerts Worst hour on one deployment
Golden Ticket Attack Detection 22,774 1,288
Windows: Suspicious PowerShell (Encoded / Download Cradle / AMSI Bypass) 1,595 286
Process Masquerading Detection 655 60
Windows: Audit Policy or Event Log Tampering 393 14

Every alert checked came from routine activity that matched a condition wider than the rule meant, and three of the rules group by computer, which stores a child alert for every event. Noise is reduced here only by de-duplication or by removing triggers that do not belong; no account, computer or application is excluded by name.

What changes

Golden Ticket Attack Detection (1.0.1 to 1.1.0) and the Windows filter's goldenTicketDetection marker (3.2.2 to 3.2.3)

  • 4768 (TGT requested) no longer triggers. Microsoft's 4768 reference says the event is written every time the KDC issues a TGT, and that a failed request has a result code other than 0x0 (with the service name krbtgt/REALM). A forged TGT is never requested from the KDC. In 30 days of real records the branch matched failed requests for unknown, disabled or expired accounts (result codes 0x6, 0x12, 0x17, where no ticket is issued and the encryption type is the placeholder 0xFFFFFFFF), and on one deployment 225,939 RC4 tickets issued to 1,387 accounts that still hold only RC4 keys. Keeping only issued RC4 tickets with per-account de-duplication would still give 271 alerts in one hour there.
  • 4672 with SeTcbPrivilege recognises the built-in identities by SID. The name check only knew the English SYSTEM, LOCAL SERVICE and NETWORK SERVICE, so SYSTEM on localized Windows (SISTEMA, Système) matched on every special logon. S-1-5-18, S-1-5-19 and S-1-5-20 are now excluded as well; the name check stays for records without a SID.
  • The 4769 branch and the history (3 matching events from one source within 30 minutes) are unchanged.
  • groupBy (source, authentication source, target user) becomes deduplicateBy: [dataSource, adversary.user]: one alert per domain controller and account, repeats dropped for seven days. The simplified authentication-source fields from fix(windows): simplify authentication correlation and repair rules that never fire #2803 are kept.
  • The filter marker that the history search counts is changed in the same way, so marker and predicate stay equal (TestWindowsRawContracts asserts this parity).

Windows: Suspicious PowerShell (1.3.0 to 1.4.0)

  • iex must be a whole word (\biex\b). As a substring it matched msiexec in a widely deployed software installer script that also calls Invoke-WebRequest; that script produced most of these alerts on three deployments.
  • FromBase64String no longer counts as the execution half of a download cradle. Decoding is not execution, and Microsoft's own scripts download and decode data: the Microsoft.Windows.NdrScanner network scanner script and the Microsoft Defender for Servers extension script account for almost all such records, and would still give 458 alerts in 30 days on one deployment (42 in its worst day). IEX, Invoke-Expression, -enc, -EncodedCommand and hidden windows still count, and the AMSI and injection markers are unchanged.
  • groupBy: [dataSource] becomes deduplicateBy: [dataSource]: one alert per computer, repeats dropped for seven days.

Process Masquerading Detection (1.0.0 to 1.1.0)

  • Paths are matched without regard to letter case. Windows writes C:\WINDOWS\System32\svchost.exe in many 4688 records, and the case-sensitive [Ww]indows check treated that as a masquerade (7,580 process creations in 30 days on two deployments, all of them legitimate Microsoft binaries).
  • The protected name must be the whole file name (gamingservices.exe from the Microsoft Store is not services.exe), and the 32-bit Explorer in C:\Windows\SysWOW64 is expected.
  • groupBy: [dataSource] becomes deduplicateBy: [dataSource, lastEvent.log.data.NewProcessName].

Windows: Audit Policy or Event Log Tampering (1.2.0 to 1.3.0)

  • Event 104 counts only from the event log service, provider Microsoft-Windows-Eventlog (the provider Microsoft's 1102 reference shows for the log-cleared events). All 6,796 event 104 records in 30 days came from other programs that reuse the number, mostly Azure AD Connect ("Directory Synchronization"). The command-line branch is unchanged.
  • groupBy: [dataSource] becomes deduplicateBy: [dataSource, lastEvent.log.providerName].

lastEvent.log.eventCode is not used as a key: it is stored as text in some daily alert indices and as a number in others, so the exact-term duplicate search would miss.

Expected volume

Replaying 30 days of real records from the 25 v11 deployments that send Windows logs with the new conditions, the Golden Ticket history and seven-day de-duplication:

Rule Alerts in 30 days (deployments) Worst hour Worst day
Golden Ticket Attack Detection 13, 5 and 4 (three deployments) 2 3
Windows: Suspicious PowerShell 3 and 1 (two deployments) 1 1
Process Masquerading Detection 0 0 0
Windows: Audit Policy or Event Log Tampering 0 0 0

Tests

  • plugins/alerts/windows_alert_volume_test.go (new) runs fabricated agent records through the Windows parser model and the pinned go-sdk v1.1.36 CEL. For each rule it pins the de-duplication keys, that they resolve to text, and records that must and must not alert: translated SYSTEM, LOCAL SERVICE and NETWORK SERVICE, failed and RC4 TGT requests, capitalised Windows paths, SysWOW64 Explorer, the Store gaming service, the installer and decoder scripts, and event 104 from Azure AD Connect and a smart-card driver. It also checks that the filter's Golden Ticket marker equals the predicate on every record. On the shipped rules and filter, 14 of its records that must not alert do alert.
  • go test ./... in plugins/alerts passes on this branch merged with current v11 (1fed8175, which includes fix(windows): simplify authentication correlation and repair rules that never fire #2803's Windows filter 3.2.2 and the other noise fixes). The only conflict was the Golden Ticket rule's version line and de-duplication keys.
  • Local EventProcessor lab (engine 8a3ade7, go-sdk v1.1.36, the production events and alerts plugins built from v11 dda9d45a, OpenSearch 2.19.1) with fabricated agent records in three runs: 15 earlier events so the Golden Ticket history is met, then 21 records of all four rules, then 5 repeats. Every run processed all of its events. Two runs of the comparison hung after writing their output (the engine's "failed to start plugin: exec: already started" restart under memory pressure on the test machine); their events and alerts were complete.
    • With this branch (rules and the filter's marker change; the lab ran before the merge with current v11): 15 alerts produced and 11 stored. Golden Ticket stored one alert each for the SeTcbPrivilege account and the failed krbtgt TGS; masquerading one per wrong-location path; PowerShell one per computer for the cradle, the Invoke-Expression script and the AMSI patch; log tampering one for the cleared log and one for wevtutil cl. Each repeat of the same computer and key in the third run was dropped as a duplicate, and a new path stored a new alert. None of the records that must not alert produced one.
    • With the rules and filter as shipped, the same inputs produced 26 alerts and stored 21 top-level alerts and 5 children, including translated SYSTEM, the failed and RC4 TGT requests, both event 104 records from other programs, the capitalised Windows paths, SysWOW64 Explorer, the Store gaming service, and the installer and decoder scripts.

🤖 Generated with Claude Code

kryonsx and others added 3 commits September 29, 2026 13:45
Four Windows rules alerted on routine activity that matched conditions
wider than they meant, and three grouped by computer, storing a child
alert for every event. Each now de-duplicates for seven days instead:

- Golden Ticket Attack Detection: 4768 no longer triggers. A forged TGT
  is never requested from the KDC; the branch matched failed requests
  (no ticket issued) and RC4 tickets issued to accounts that only hold
  RC4 keys. SYSTEM, LOCAL SERVICE and NETWORK SERVICE are recognised by
  SID (S-1-5-18/19/20), so localized names such as SISTEMA no longer
  match 4672. deduplicateBy dataSource, adversary.user. The filter's
  goldenTicketDetection marker changes the same way (filter 3.2.1).
- Suspicious PowerShell: iex must be a whole word (it matched msiexec),
  and FromBase64String alone is no longer the execution half of a
  download cradle. deduplicateBy dataSource.
- Process Masquerading: paths match without regard to case
  (C:\WINDOWS\System32), the protected name must be the whole file
  name, and SysWOW64 Explorer is expected. deduplicateBy dataSource,
  lastEvent.log.data.NewProcessName.
- Audit Policy or Event Log Tampering: event 104 counts only from
  Microsoft-Windows-Eventlog; Azure AD Connect reuses the number.
  deduplicateBy dataSource, lastEvent.log.providerName.

windows_alert_volume_test.go pins the keys, the records that must and
must not alert, and the parity of the Golden Ticket marker.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…hange

v11 now carries filter 3.2.1 (actionResult values); the Golden Ticket
marker change becomes 3.2.2.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@kryonsx
kryonsx marked this pull request as ready for review October 1, 2026 02:04
@kryonsx
kryonsx requested a review from a team October 1, 2026 02:04
v11 now carries utmstack#2803, which also changed the Golden Ticket rule (v1.0.1)
and the Windows filter (3.2.2). The rule keeps this branch's conditions,
version v1.1.0 and deduplicateBy dataSource, adversary.user on top of
utmstack#2803's simplified authentication-source fields. The filter's Golden
Ticket marker change becomes version 3.2.3.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant