Adversaries may attempt to log in using expired accounts to bypass authentication controls and gain unauthorized access. SOC teams should proactively hunt for this behavior in Azure Sentinel to identify potential credential reuse or persistence tactics early.
KQL Query
(union isfuzzy=true
(SecurityEvent
| where EventID == 4625
//4625: An account failed to log on
| where AccountType == 'User'
| where SubStatus == '0xc0000193'
| extend Reason =
case
( SubStatus == '0xc0000193', 'Windows EventID (4625) - Account has expired', "Unknown")
| project Computer, Account, Reason , TimeGenerated
),
(
SecurityEvent
| where EventID == 4769
//4769: A Kerberos service ticket was requested ( Kerberos Auth)
| parse EventData with * 'Status">' Status "<" *
| parse EventData with * 'TargetUserName">' TargetUserName "<" *
| where Status == '0x12'
| where TargetUserName !has "$" and isnotempty(TargetUserName)
| extend Reason =
case(
Status == '0x12', 'Windows EventID (4769) - Account disabled, expired, locked out',
'Unknown'), Account = TargetUserName
| project Computer, Account, Reason , TimeGenerated
),
(
SecurityEvent
| where EventID == 4776
// 4776: The domain controller attempted to validate the credentials for an account ( NTLM Auth)
| where Status == "0xc0000193"
| extend Reason =
case(
ErrorCode == '0xc0000193', 'Windows EventID (4776) - Account has expired',
'Unknown'), Account = TargetAccount
| parse EventData with * 'Workstation">' Workstation "<" *
| extend Workstation = trim_start(@"[\\]*", Workstation)
| extend Computer = iff(isnotempty(Workstation), Workstation, Computer )
| project Computer, Account, Reason , TimeGenerated
) ,
(
SigninLogs
| where ResultType == "50057"
| extend Reason =
case(
ResultType == '50057', 'SigninLogs( Result Code- 50057) - User account is disabled. The account has been disabled by an administrator.',
'Unknown'), Account = UserPrincipalName
| project Account, Reason , TimeGenerated
) )
| summarize StartTimeUtc = min(TimeGenerated), EndTImeUtc = max(TimeGenerated), EventCount = count() by Computer, Account, Reason
| extend timestamp = StartTimeUtc, AccountCustomEntity = Account, HostCustomEntity = Computer
| order by EventCount desc
id: 562900b1-39c4-4baf-a050-9cad1641db35
name: Failed Login Attempt by Expired account
description: |
'This query looks at Account Logon events found through Windows Event Id's as well as SigninLogs to discover
login attempts by accounts that have expired.'
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- SigninLogs
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
tactics:
- InitialAccess
relevantTechniques:
- T1078
query: |
(union isfuzzy=true
(SecurityEvent
| where EventID == 4625
//4625: An account failed to log on
| where AccountType == 'User'
| where SubStatus == '0xc0000193'
| extend Reason =
case
( SubStatus == '0xc0000193', 'Windows EventID (4625) - Account has expired', "Unknown")
| project Computer, Account, Reason , TimeGenerated
),
(
SecurityEvent
| where EventID == 4769
//4769: A Kerberos service ticket was requested ( Kerberos Auth)
| parse EventData with * 'Status">' Status "<" *
| parse EventData with * 'TargetUserName">' TargetUserName "<" *
| where Status == '0x12'
| where TargetUserName !has "$" and isnotempty(TargetUserName)
| extend Reason =
case(
Status == '0x12', 'Windows EventID (4769) - Account disabled, expired, locked out',
'Unknown'), Account = TargetUserName
| project Computer, Account, Reason , TimeGenerated
),
(
SecurityEvent
| where EventID == 4776
// 4776: The domain controller attempted to validate the credentials for an account ( NTLM Auth)
| where Status == "0xc0000193"
| extend Reason =
case(
ErrorCode == '0xc0000193', 'Windows EventID (4776) - Account has expired',
'Unknown'), Account = TargetAccount
| parse EventData with * 'Workstation">' Workstation "<" *
| extend Workstation = trim_start(@"[\\]*", Workstation)
| extend Computer = iff(isnotempty(Workstation), Workstation, Computer )
| project Computer, Account, Reason , TimeGenerated
) ,
(
SigninLogs
| where ResultType == "50057"
| extend Reason =
case(
ResultType == '50057', 'SigninLogs( Result Code- 50057) - User account is disabled. The account has been disabled by an administrator.',
'Unknown'), Account = UserPrincipalName
| project Account, Reason , TimeGenerated
) )
| summarize StartTimeUtc = min(TimeGenerated), EndTImeUtc = max(TimeGenerated), EventCount = count() by Computer, Account, Reason
| extend timestamp = StartTimeUtc, AccountCustomEntity = Account, HostCustomEntity = Computer
| order by EventCount desc
version: 1.0.1
metadata:
source:
kind: Community
author:
name: Shain
support:
tier: Community
categories:
domains: [ "Security - Other", "Identity" ]
| Sentinel Table | Notes |
|---|---|
SecurityEvent | Ensure this data connector is enabled |
SigninLogs | Ensure this data connector is enabled |
Scenario: Scheduled Job Using an Expired Account
Description: A legacy scheduled job (e.g., via Task Scheduler or SQL Agent) is configured to run under an expired account, which is still referenced in the system.
Filter/Exclusion: Check for EventID 41 (Task Scheduler) or EventID 1551 (SQL Server Agent) with the account name, and exclude accounts marked as expired in Active Directory.
Scenario: System Maintenance Task Using an Expired Admin Account
Description: An old administrative account (e.g., admin_old) was used for system maintenance tasks and was later disabled. The account is still being referenced in scripts or tools like PowerShell or SCCM.
Filter/Exclusion: Exclude accounts that are flagged as disabled or expired in Active Directory, or filter by EventID 4624 with Logon Type 10 (Interactive) or Logon Type 11 (Network).
Scenario: Legacy Application Using an Expired Service Account
Description: A legacy application (e.g., IIS, Exchange, or a custom service) is still using an expired service account for authentication.
Filter/Exclusion: Filter by EventID 4624 with Logon Type 3 (Network) and exclude accounts that are marked as expired in the domain or are known service accounts.
Scenario: User Testing with an Expired Test Account
Description: A test account (e.g., test_user) was created for testing purposes and was later expired, but the user is still attempting to log in using it.
Filter/Exclusion: Exclude accounts that are marked as expired in Active Directory or are associated with test environments (e.g., test_* or dev_*).
**Scenario: