Detection rules › Kusto
AD account with Don't Expire Password
Identifies whenever a user account has the setting "Password Never Expires" in the user account properties selected. This is indicated in Security event 4738 in the EventData item labeled UserAccountControl with an included value of %%2089. %%2089 resolves to "Don't Expire Password - Enabled".
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Persistence |
Telemetry coverage
| Provider | Record / event type |
|---|---|
| Security-Auditing | Event ID 4738: A user account was changed. |
Rule body
id: 6c360107-f3ee-4b91-9f43-f4cfd90441cf
name: AD account with Don't Expire Password
description: |
'Identifies whenever a user account has the setting "Password Never Expires" in the user account properties selected.
This is indicated in Security event 4738 in the EventData item labeled UserAccountControl with an included value of %%2089.
%%2089 resolves to "Don't Expire Password - Enabled".'
severity: Low
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
- connectorId: WindowsSecurityEvents
dataTypes:
- SecurityEvent
- connectorId: WindowsForwardedEvents
dataTypes:
- WindowsEvent
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Persistence
relevantTechniques:
- T1098
query: |
union isfuzzy=true
(
SecurityEvent
| where EventID == 4738
// 2089 value indicates the Don't Expire Password value has been set
| where UserAccountControl has "%%2089"
| extend Value_2089 = iff(UserAccountControl has "%%2089","'Don't Expire Password' - Enabled", "Not Changed")
// 2050 indicates that the Password Not Required value is NOT set, this often shows up at the same time as a 2089 and is the recommended value. This value may not be in the event.
| extend Value_2050 = iff(UserAccountControl has "%%2050","'Password Not Required' - Disabled", "Not Changed")
// If value %%2082 is present in the 4738 event, this indicates the account has been configured to logon WITHOUT a password. Generally you should only see this value when an account is created and only in Event 4720: Account Creation Event.
| extend Value_2082 = iff(UserAccountControl has "%%2082","'Password Not Required' - Enabled", "Not Changed")
| project StartTime = TimeGenerated, EventID, Activity, Computer, TargetAccount, TargetUserName, TargetDomainName, TargetSid,
AccountType, UserAccountControl, Value_2089, Value_2050, Value_2082, SubjectAccount, SubjectUserName, SubjectDomainName, SubjectUserSid
),
(
WindowsEvent
| where EventID == 4738 and EventData has '2089'
// 2089 value indicates the Don't Expire Password value has been set
| extend UserAccountControl = tostring(EventData.UserAccountControl)
| where UserAccountControl has "%%2089"
| extend Value_2089 = iff(UserAccountControl has "%%2089","'Don't Expire Password' - Enabled", "Not Changed")
// 2050 indicates that the Password Not Required value is NOT set, this often shows up at the same time as a 2089 and is the recommended value. This value may not be in the event.
| extend Value_2050 = iff(UserAccountControl has "%%2050","'Password Not Required' - Disabled", "Not Changed")
// If value %%2082 is present in the 4738 event, this indicates the account has been configured to logon WITHOUT a password. Generally you should only see this value when an account is created and only in Event 4720: Account Creation Event.
| extend Value_2082 = iff(UserAccountControl has "%%2082","'Password Not Required' - Enabled", "Not Changed")
| extend Activity="4738 - A user account was changed."
| extend TargetAccount = strcat(EventData.TargetDomainName,"\\", EventData.TargetUserName)
| extend TargetSid = tostring(EventData.TargetSid)
| extend SubjectAccount = strcat(EventData.SubjectDomainName,"\\", EventData.SubjectUserName)
| extend SubjectUserSid = tostring(EventData.SubjectUserSid)
| extend AccountType=case(SubjectAccount endswith "$" or SubjectUserSid in ("S-1-5-18", "S-1-5-19", "S-1-5-20"), "Machine", isempty(SubjectUserSid), "", "User")
| project StartTime = TimeGenerated, EventID, Activity, Computer, TargetAccount, TargetUserName = tostring(EventData.TargetUserName), TargetDomainName = tostring(EventData.TargetDomainName), TargetSid,
AccountType, UserAccountControl, Value_2089, Value_2050, Value_2082, SubjectAccount, SubjectDomainName = tostring(EventData.SubjectDomainName), SubjectUserName = tostring(EventData.SubjectUserName), SubjectUserSid = tostring(EventData.SubjectUserSid)
)
| extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
| extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
| project-away DomainIndex
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: TargetAccount
- identifier: Name
columnName: TargetUserName
- identifier: NTDomain
columnName: TargetDomainName
- entityType: Account
fieldMappings:
- identifier: Sid
columnName: TargetSid
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: SubjectAccount
- identifier: Name
columnName: SubjectUserName
- identifier: NTDomain
columnName: SubjectDomainName
- entityType: Account
fieldMappings:
- identifier: Sid
columnName: SubjectUserSid
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: Computer
- identifier: HostName
columnName: HostName
- identifier: DnsDomain
columnName: HostNameDomain
version: 1.2.2
kind: Scheduled
metadata:
source:
kind: Community
author:
name: Microsoft Security Research
support:
tier: Community
categories:
domains: [ "Security - Others", "Identity" ]
Stages and Predicates
Stage 1: union
union of 2 branches
Stage 2: source
SecurityEvent
Stage 3: where
| where EventID == 4738
Stage 4: where
| where UserAccountControl has "%%2089"
Stage 5: extend (3 consecutive steps)
| extend Value_2089 = iff(UserAccountControl has "%%2089","'Don't Expire Password' - Enabled", "Not Changed")
| extend Value_2050 = iff(UserAccountControl has "%%2050","'Password Not Required' - Disabled", "Not Changed")
| extend Value_2082 = iff(UserAccountControl has "%%2082","'Password Not Required' - Enabled", "Not Changed")
Value_2089 =if
UserAccountControl has "%%2089""'Don't Expire Password' - Enabled"else
"Not Changed"Stage 6: project
| project StartTime = TimeGenerated, EventID, Activity, Computer, TargetAccount, TargetUserName, TargetDomainName, TargetSid,
AccountType, UserAccountControl, Value_2089, Value_2050, Value_2082, SubjectAccount, SubjectUserName, SubjectDomainName, SubjectUserSid
Stage 7: source
WindowsEvent
Stage 8: where
| where EventID == 4738 and EventData has '2089'
Stage 9: extend
| extend UserAccountControl = tostring(EventData.UserAccountControl)
Stage 10: where
| where UserAccountControl has "%%2089"
Stage 11: extend (9 consecutive steps)
| extend Value_2089 = iff(UserAccountControl has "%%2089","'Don't Expire Password' - Enabled", "Not Changed")
| extend Value_2050 = iff(UserAccountControl has "%%2050","'Password Not Required' - Disabled", "Not Changed")
| extend Value_2082 = iff(UserAccountControl has "%%2082","'Password Not Required' - Enabled", "Not Changed")
| extend Activity="4738 - A user account was changed."
| extend TargetAccount = strcat(EventData.TargetDomainName,"\\", EventData.TargetUserName)
| extend TargetSid = tostring(EventData.TargetSid)
| extend SubjectAccount = strcat(EventData.SubjectDomainName,"\\", EventData.SubjectUserName)
| extend SubjectUserSid = tostring(EventData.SubjectUserSid)
| extend AccountType=case(SubjectAccount endswith "$" or SubjectUserSid in ("S-1-5-18", "S-1-5-19", "S-1-5-20"), "Machine", isempty(SubjectUserSid), "", "User")
Value_2089 =if
UserAccountControl has "%%2089""'Don't Expire Password' - Enabled"else
"Not Changed"Stage 12: project
| project StartTime = TimeGenerated, EventID, Activity, Computer, TargetAccount, TargetUserName = tostring(EventData.TargetUserName), TargetDomainName = tostring(EventData.TargetDomainName), TargetSid,
AccountType, UserAccountControl, Value_2089, Value_2050, Value_2082, SubjectAccount, SubjectDomainName = tostring(EventData.SubjectDomainName), SubjectUserName = tostring(EventData.SubjectUserName), SubjectUserSid = tostring(EventData.SubjectUserSid)
Stage 13: extend
extend DomainIndex, HostName
Stage 14: extend
extend HostNameDomain
HostNameDomain =if
DomainIndex != -1substring(Computer, (DomainIndex + 1))else
ComputerStage 15: project-away
project-away DomainIndex
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventData | match |
| field:"EventData" kind:match value:"2089" |
EventID | eq |
| field:"EventID" kind:eq value:"4738" |
UserAccountControl | match |
| field:"UserAccountControl" kind:match value:"%%2089" |
Output fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountType | project |
Activity | project |
Computer | project |
EventID | project |
StartTime | project |
SubjectAccount | project |
SubjectDomainName | project |
SubjectUserName | project |
SubjectUserSid | project |
TargetAccount | project |
TargetDomainName | project |
TargetSid | project |
TargetUserName | project |
UserAccountControl | project |
Value_2050 | project |
Value_2082 | project |
Value_2089 | project |
HostName | extend |
HostNameDomain | extend |