Detection rules › Splunk
Windows Suspicious Burst of Password Changes
A regular user account performed rapid, repeated password changes across multiple local accounts within a 2-second window. This pattern is consistent with automated credential manipulation tools that cycle account passwords to deny access to defenders or escalate privileges The speed and volume of changes indicates scripted or tooled activity rather than manual administration, as legitimate password resets do not occur at machine speed across multiple accounts simultaneously.
Known false positives
- Service accounts may be responsible for the creation, deletion or modification of accounts for legitimate purposes. Filter as needed.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Privilege Escalation |
Telemetry coverage
Rule body
name: Windows Suspicious Burst of Password Changes
id: 0dcfcab9-e936-43bc-8203-005e17dc0744
version: 1
creation_date: '2026-04-29'
modification_date: '2026-04-29'
author: Raven Tait, Splunk
status: production
type: TTP
description: |-
A regular user account performed rapid, repeated password changes across multiple local accounts within a 2-second window.
This pattern is consistent with automated credential manipulation tools that cycle account passwords to deny access to defenders or escalate privileges The speed and volume of changes indicates scripted or tooled activity rather than manual administration, as legitimate password resets do not occur at machine speed across multiple accounts simultaneously.
data_source:
- Windows Event Log Security 4723
- Windows Event Log Security 4724
search: |-
`wineventlog_security`
(
EventCode=4723
OR
EventCode=4724
)
| eval log_time = strptime(SystemTime, "%Y-%m-%dT%H:%M:%S.%6NZ")
| eval log_time = coalesce(log_time, _time)
| eval time_bucket = floor(log_time / 2) * 2
| eval SubjectRID = tonumber(replace(SubjectUserSid, ".*-(\d+)$", "\1"))
| where SubjectRID >= 1000
| stats count AS EventsForAccount
min(log_time) AS firstTime
max(log_time) AS lastTime
by Computer TargetUserName SubjectUserName time_bucket
| where EventsForAccount >= 4
| eval DurationSecs = round(lastTime - firstTime, 3), DurationSecs = if(DurationSecs = 0, 0.001, DurationSecs), ChangeRatePerSec = round(EventsForAccount / DurationSecs, 2)
| rename Computer as dest
| stats dc(TargetUserName) AS AffectedAccounts
values(TargetUserName) AS Accounts
values(SubjectUserName) AS InitiatingAccounts
sum(EventsForAccount) AS TotalEvents
max(ChangeRatePerSec) AS PeakChangeRatePerSec
min(DurationSecs) AS FastestBurstSecs
min(firstTime) AS firstTime
max(lastTime) AS lastTime
by dest time_bucket
| `security_content_ctime(firstTime)`
| `security_content_ctime(lastTime)`
| `windows_suspicious_burst_of_password_changes_filter`
how_to_implement: |-
To successfully implement this search, you need to be ingesting EventID 4723 and EventID 4724 events with the Windows TA.
known_false_positives: |-
Service accounts may be responsible for the creation, deletion or modification of accounts for legitimate purposes. Filter as needed.
references:
- https://github.com/Nightmare-Eclipse/BlueHammer
finding:
title: Rapid burst of password changes of local accounts by $InitiatingAccounts$ on $dest$
entity:
field: dest
type: system
score: 20
analytic_story:
- Windows Privilege Escalation
- BlueHammer
asset_type: Endpoint
mitre_attack_id:
- T1068
product:
- Splunk Enterprise
- Splunk Enterprise Security
- Splunk Cloud
category: endpoint
security_domain: endpoint
cve:
- CVE-2026-33825
Stages and Predicates
Stage 1: search
`wineventlog_security`
(
EventCode=4723
OR
EventCode=4724
)
Stage 2: eval
| eval log_time = strptime(SystemTime, "%Y-%m-%dT%H:%M:%S.%6NZ")
Stage 3: eval
| eval log_time = coalesce(log_time, _time)
Stage 4: eval
| eval time_bucket = floor(log_time / 2) * 2
Stage 5: eval
| eval SubjectRID = tonumber(replace(SubjectUserSid, ".*-(\d+)$", "\1"))
Stage 6: where
| where SubjectRID >= 1000
Stage 7: stats
| stats count AS EventsForAccount
min(log_time) AS firstTime
max(log_time) AS lastTime
by Computer TargetUserName SubjectUserName time_bucket
Stage 8: where
| where EventsForAccount >= 4
Stage 9: eval
| eval DurationSecs = round(lastTime - firstTime, 3), DurationSecs = if(DurationSecs = 0, 0.001, DurationSecs), ChangeRatePerSec = round(EventsForAccount / DurationSecs, 2)
DurationSecs =if
DurationSecs = 00.001else
DurationSecsStage 10: rename
| rename Computer as dest
Stage 11: stats
| stats dc(TargetUserName) AS AffectedAccounts
values(TargetUserName) AS Accounts
values(SubjectUserName) AS InitiatingAccounts
sum(EventsForAccount) AS TotalEvents
max(ChangeRatePerSec) AS PeakChangeRatePerSec
min(DurationSecs) AS FastestBurstSecs
min(firstTime) AS firstTime
max(lastTime) AS lastTime
by dest time_bucket
Stage 12: search
| `security_content_ctime(firstTime)`
Stage 13: search
| `security_content_ctime(lastTime)`
Stage 14: search
| `windows_suspicious_burst_of_password_changes_filter`
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventCode | eq |
| field:"EventID" kind:eq |
EventsForAccount | ge |
| field:"EventsForAccount" kind:ge value:"4" |
SubjectRID | ge |
| field:"SubjectRID" kind:ge value:"1000" |