Kusto Query LanguageMicrosoft Defender for EndpointMicrosoft SentinelSECURE

Investigate Brute Force Logins Followed by Password Changes using KQL

Adversaries gaining access through brute force may immediately change a compromised account’s password to maintain persistence without raising alarms. This detection logic uses KQL to correlate high volumes of failed login attempts, followed by a successful login, then a password change within a short window. By leveraging custom thresholds for failed and successful logons, along with defined time windows, this approach helps isolate accounts that are likely victims of credential attacks followed by unauthorized account modifications.

Risk

When attackers perform a brute force attack and successfully gain access, a common tactic is to change the compromised account’s password. This ensures they retain access while locking out the legitimate user. Left undetected, it provides adversaries long-term control over internal systems or sensitive data.

Query

Microsoft Defender For Endpoint

Kusto
let FailedLogonsThreshold = 20;
let SuccessfulLogonsThreshold = 1;
let TimeWindow = 15m;
// Time between the succesful brute force and password change. Difference should be added in minutes
let SearchWindow = 120;
IdentityLogonEvents
// Filter emtpy UPN
| where isnotempty(AccountUpn)
| summarize
    TotalAttempts = count(),
    SuccessfulAttempts = countif(ActionType == "LogonSuccess"),
    FailedAttempts = countif(ActionType == "LogonFailed")
    by bin(Timestamp, TimeWindow), AccountUpn
// Use variables to define brute force attack
| where SuccessfulAttempts >= SuccessfulLogonsThreshold and FailedAttempts >= FailedLogonsThreshold
// join password changes
| join kind=inner (IdentityDirectoryEvents
    | where Timestamp > ago(30d)
    | where ActionType == "Account Password changed"
    | where isnotempty(TargetAccountUpn)
    | extend PasswordChangeTime = Timestamp
    | project PasswordChangeTime, TargetAccountUpn)
    on $left.AccountUpn == $right.TargetAccountUpn
// Collect timedifference between brute force (note that is uses the bin time) and the password change
| extend TimeDifference = datetime_diff('minute', PasswordChangeTime, Timestamp)
// Remove all entries where the password change took place before the brute force
| where TimeDifference > 0
| where TimeDifference <= SearchWindow

Microsoft Sentinel

Kusto
let FailedLogonsThreshold = 20;
let SuccessfulLogonsThreshold = 1;
let TimeWindow = 15m;
// Time between the succesful brute force and password change. Difference should be added in minutes
let SearchWindow = 120;
IdentityLogonEvents
// Filter emtpy UPN
| where isnotempty(AccountUpn)
| summarize
    TotalAttempts = count(),
    SuccessfulAttempts = countif(ActionType == "LogonSuccess"),
    FailedAttempts = countif(ActionType == "LogonFailed")
    by bin(TimeGenerated, TimeWindow), AccountUpn
// Use variables to define brute force attack
| where SuccessfulAttempts >= SuccessfulLogonsThreshold and FailedAttempts >= FailedLogonsThreshold
// join password changes
| join kind=inner (IdentityDirectoryEvents
    | where TimeGenerated > ago(30d)
    | where ActionType == "Account Password changed"
    | where isnotempty(TargetAccountUpn)
    | extend PasswordChangeTime = TimeGenerated
    | project PasswordChangeTime, TargetAccountUpn)
    on $left.AccountUpn == $right.TargetAccountUpn
// Collect timedifference between brute force (note that is uses the bin time) and the password change
| extend TimeDifference = datetime_diff('minute', PasswordChangeTime, TimeGenerated)
// Remove all entries where the password change took place before the brute force
| where TimeDifference > 0
| where TimeDifference <= SearchWindow

This query uses customizable variables:

  • FailedLogonsThreshold: Minimum failed login attempts (e.g., 20).
  • SuccessfulLogonsThreshold: Minimum successful logins (e.g., 1).
  • TimeWindow: The rolling time window to detect brute force patterns (e.g., 15 minutes).
  • SearchWindow: Duration to catch password changes post-successful login (e.g., 120 minutes).

References

Leave a Reply

Your email address will not be published. Required fields are marked *