Detection rules › Kusto
User account enabled and disabled within 10 mins
Identifies when a user account is enabled and then disabled within 10 minutes. This can be an indication of compromise and an adversary attempting to hide in the noise.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Persistence | |
| Privilege Escalation |
Telemetry coverage
Rule body
id: 3d023f64-8225-41a2-9570-2bd7c2c4535e
name: User account enabled and disabled within 10 mins
description: |
'Identifies when a user account is enabled and then disabled within 10 minutes. This can be an indication of compromise and an adversary attempting to hide in the noise.'
severity: Medium
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
- connectorId: WindowsSecurityEvents
dataTypes:
- SecurityEvents
- connectorId: WindowsForwardedEvents
dataTypes:
- WindowsEvent
queryFrequency: 1d
queryPeriod: 25h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Persistence
- PrivilegeEscalation
relevantTechniques:
- T1098
- T1078
query: |
let timeframe = 1d;
let spanoftime = 10m;
let threshold = 0;
(union isfuzzy=true
(SecurityEvent
| where TimeGenerated > ago(timeframe+spanoftime)
// A user account was enabled
| where EventID == 4722
| where AccountType =~ "User"
| where TargetAccount !endswith "$"
| project EnableTime = TimeGenerated, EnableEventID = EventID, EnableActivity = Activity, Computer = toupper(Computer),
TargetAccount = tolower(TargetAccount), TargetUserName, TargetDomainName, TargetSid,
AccountUsedToEnable = SubjectAccount, EnabledBySubjectUserName = SubjectUserName, EnabledBySubjectDomainName = SubjectDomainName, SIDofAccountUsedToEnable = SubjectUserSid, UserPrincipalName
),
(
WindowsEvent
| where TimeGenerated > ago(timeframe+spanoftime)
// A user account was enabled
| where EventID == 4722
| extend SubjectUserSid = tostring(EventData.SubjectUserSid)
| extend AccountType=case(EventData.SubjectUserName endswith "$" or SubjectUserSid in ("S-1-5-18", "S-1-5-19", "S-1-5-20"), "Machine", isempty(SubjectUserSid), "", "User")
| where AccountType =~ "User"
| extend SubjectAccount = strcat(tostring(EventData.SubjectDomainName),"\\", tostring(EventData.SubjectUserName))
| extend SubjectDomainName = tostring(EventData.SubjectDomainName), SubjectUserName = tostring(EventData.SubjectUserName)
| extend TargetAccount = strcat(EventData.TargetDomainName,"\\", EventData.TargetUserName)
| where TargetAccount !endswith "$"
| extend Activity="4722 - A user account was enabled."
| extend TargetUserName = tostring(EventData.TargetUserName), TargetDomainName = tostring(EventData.TargetDomainName)
| extend TargetSid = tostring(EventData.TargetSid)
| extend UserPrincipalName = tostring(EventData.UserPrincipalName)
| project EnableTime = TimeGenerated, EnableEventID = EventID, EnableActivity = Activity, Computer = toupper(Computer),
TargetAccount = tolower(TargetAccount), TargetUserName, TargetDomainName, TargetSid,
AccountUsedToEnable = SubjectAccount, EnabledBySubjectUserName = SubjectUserName, EnabledBySubjectDomainName = SubjectDomainName, SIDofAccountUsedToEnable = SubjectUserSid, UserPrincipalName
)
)
| join kind= inner (
(union isfuzzy=true
(SecurityEvent
| where TimeGenerated > ago(timeframe)
// A user account was disabled
| where EventID == 4725
| where AccountType =~ "User"
| project DisableTime = TimeGenerated, DisableEventID = EventID, DisableActivity = Activity, Computer = toupper(Computer),
TargetAccount = tolower(TargetAccount), TargetUserName, TargetDomainName, TargetSid,
AccountUsedToDisable = SubjectAccount, DisabledBySubjectUserName = SubjectUserName, DisabledBySubjectDomainName = SubjectDomainName, SIDofAccountUsedToDisable = SubjectUserSid, UserPrincipalName
),
(WindowsEvent
| where TimeGenerated > ago(timeframe)
// A user account was disabled
| where EventID == 4725
| extend SubjectUserSid = tostring(EventData.SubjectUserSid)
| extend AccountType=case(EventData.SubjectUserName endswith "$" or SubjectUserSid in ("S-1-5-18", "S-1-5-19", "S-1-5-20"), "Machine", isempty(SubjectUserSid), "", "User")
| where AccountType =~ "User"
| extend SubjectAccount = strcat(tostring(EventData.SubjectDomainName),"\\", tostring(EventData.SubjectUserName))
| extend SubjectDomainName = tostring(EventData.SubjectDomainName), SubjectUserName = tostring(EventData.SubjectUserName)
| extend TargetAccount = strcat(EventData.TargetDomainName,"\\", EventData.TargetUserName)
| extend TargetUserName = tostring(EventData.TargetUserName), TargetDomainName = tostring(EventData.TargetDomainName)
| extend TargetSid = tostring(EventData.TargetSid)
| extend UserPrincipalName = tostring(EventData.UserPrincipalName)
| extend Activity = "4725 - A user account was disabled."
| project DisableTime = TimeGenerated, DisableEventID = EventID, DisableActivity = Activity, Computer = toupper(Computer),
TargetAccount = tolower(TargetAccount), TargetUserName, TargetDomainName, TargetSid,
AccountUsedToDisable = SubjectAccount, DisabledBySubjectUserName = SubjectUserName, DisabledBySubjectDomainName = SubjectDomainName, SIDofAccountUsedToDisable = SubjectUserSid, UserPrincipalName
)
)
) on Computer, TargetAccount
| where DisableTime - EnableTime < spanoftime
| extend TimeDelta = DisableTime - EnableTime
| where tolong(TimeDelta) >= threshold
| project TimeDelta, EnableTime, EnableEventID, EnableActivity, Computer, TargetAccount, TargetSid, TargetUserName, TargetDomainName, UserPrincipalName,
AccountUsedToEnable, SIDofAccountUsedToEnable, DisableTime, DisableEventID, DisableActivity, AccountUsedToDisable, SIDofAccountUsedToDisable,
EnabledBySubjectUserName, EnabledBySubjectDomainName, DisabledBySubjectUserName, DisabledBySubjectDomainName
| 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: AccountUsedToEnable
- identifier: Name
columnName: EnabledBySubjectUserName
- identifier: NTDomain
columnName: EnabledBySubjectDomainName
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: AccountUsedToDisable
- identifier: Name
columnName: DisabledBySubjectUserName
- identifier: NTDomain
columnName: DisabledBySubjectDomainName
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: TargetAccount
- identifier: Name
columnName: TargetUserName
- identifier: NTDomain
columnName: TargetDomainName
- entityType: Account
fieldMappings:
- identifier: Sid
columnName: TargetSid
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: Computer
- identifier: HostName
columnName: HostName
- identifier: NTDomain
columnName: HostNameDomain
version: 1.2.3
kind: Scheduled
metadata:
source:
kind: Community
author:
name: Microsoft Security Research
support:
tier: Community
categories:
domains: [ "Security - Others", "Identity" ]
Stages and Predicates
Stage 0: let
let timeframe = 1d;
let spanoftime = 10m;
let threshold = 0;
Stage 1: union
union of 2 branches
Stage 2: source
SecurityEvent
Stage 3: where
| where TimeGenerated > ago(timeframe+spanoftime)
Stage 4: where
| where EventID == 4722
Stage 5: where
| where AccountType =~ "User"
Stage 6: where
| where TargetAccount !endswith "$"
Stage 7: project
| project EnableTime = TimeGenerated, EnableEventID = EventID, EnableActivity = Activity, Computer = toupper(Computer),
TargetAccount = tolower(TargetAccount), TargetUserName, TargetDomainName, TargetSid,
AccountUsedToEnable = SubjectAccount, EnabledBySubjectUserName = SubjectUserName, EnabledBySubjectDomainName = SubjectDomainName, SIDofAccountUsedToEnable = SubjectUserSid, UserPrincipalName
Stage 8: source
WindowsEvent
Stage 9: where
| where TimeGenerated > ago(timeframe+spanoftime)
Stage 10: where
| where EventID == 4722
Stage 11: extend
| extend SubjectUserSid = tostring(EventData.SubjectUserSid)
Stage 12: extend
| extend AccountType=case(EventData.SubjectUserName endswith "$" or SubjectUserSid in ("S-1-5-18", "S-1-5-19", "S-1-5-20"), "Machine", isempty(SubjectUserSid), "", "User")
AccountType =if
(SubjectUserName endswith "$" or SubjectUserSid in ("S-1-5-18", "S-1-5-19", "S-1-5-20"))"Machine"elif
isempty(SubjectUserSid)""else
"User"Stage 13: where
| where AccountType =~ "User"
Stage 14: extend (3 consecutive steps)
| extend SubjectAccount = strcat(tostring(EventData.SubjectDomainName),"\\", tostring(EventData.SubjectUserName))
| extend SubjectDomainName = tostring(EventData.SubjectDomainName), SubjectUserName = tostring(EventData.SubjectUserName)
| extend TargetAccount = strcat(EventData.TargetDomainName,"\\", EventData.TargetUserName)
Stage 15: where
| where TargetAccount !endswith "$"
Stage 16: extend (4 consecutive steps)
| extend Activity="4722 - A user account was enabled."
| extend TargetUserName = tostring(EventData.TargetUserName), TargetDomainName = tostring(EventData.TargetDomainName)
| extend TargetSid = tostring(EventData.TargetSid)
| extend UserPrincipalName = tostring(EventData.UserPrincipalName)
Stage 17: project
| project EnableTime = TimeGenerated, EnableEventID = EventID, EnableActivity = Activity, Computer = toupper(Computer),
TargetAccount = tolower(TargetAccount), TargetUserName, TargetDomainName, TargetSid,
AccountUsedToEnable = SubjectAccount, EnabledBySubjectUserName = SubjectUserName, EnabledBySubjectDomainName = SubjectDomainName, SIDofAccountUsedToEnable = SubjectUserSid, UserPrincipalName
Stage 18: join
join kind=inner (...) on Computer, TargetAccount
Stage 19: where DisableTime - EnableTime < 10m
where DisableTime - EnableTime < 10m
Stage 20: extend
extend TimeDelta
Stage 21: where
where TimeDelta >= 0
Stage 22: project
project AccountUsedToDisable, AccountUsedToEnable, Computer, DisableActivity, DisableEventID, DisableTime, DisabledBySubjectDomainName, DisabledBySubjectUserName, EnableActivity, EnableEventID, EnableTime, EnabledBySubjectDomainName, EnabledBySubjectUserName, SIDofAccountUsedToDisable, SIDofAccountUsedToEnable, TargetAccount, TargetDomainName, TargetSid, TargetUserName, TimeDelta, UserPrincipalName
Stage 23: extend
extend DomainIndex, HostName
Stage 24: extend
extend HostNameDomain
HostNameDomain =if
DomainIndex != -1substring(Computer, (DomainIndex + 1))else
ComputerStage 25: project-away
project-away DomainIndex
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
AccountType | eq |
| field:"AccountType" kind:eq value:"User" |
EventID | eq |
| field:"EventID" kind:eq |
TimeDelta | ge |
| field:"TimeDelta" kind:ge value:"0" |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
TargetAccount | ends_with | $ | excludes:TargetAccount field:"TargetAccount" value:"$" |
Output fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountUsedToDisable | project |
AccountUsedToEnable | project |
Computer | project |
DisableActivity | project |
DisableEventID | project |
DisableTime | project |
DisabledBySubjectDomainName | project |
DisabledBySubjectUserName | project |
EnableActivity | project |
EnableEventID | project |
EnableTime | project |
EnabledBySubjectDomainName | project |
EnabledBySubjectUserName | project |
SIDofAccountUsedToDisable | project |
SIDofAccountUsedToEnable | project |
TargetAccount | project |
TargetDomainName | project |
TargetSid | project |
TargetUserName | project |
TimeDelta | project |
UserPrincipalName | project |
HostName | extend |
HostNameDomain | extend |